Javascript:按文件路径比较两个选定的文件

问题描述 投票:0回答:1

我正在寻找一种方法来检查两个文件/文件(PDF,JPG,PNG)是否相同。

如果用户选择一个或多个文件,我将文件对象转换为javascript对象。我保持大小,类型,文件名,然后创建一个blob,这样我就可以将对象存储在我的redux商店中。

当用户选择另一个文件时,我想将此文件与已添加的文件进行比较(因此我可以设置相同的blobURL)。

我可以检查两个文件是否具有相同的名称,类型和大小,但是有一个更改,所有这些属性匹配,文件不一样,所以我想检查文件路径。遗憾的是,文件对象中未提供该属性。有没有办法得到这个或另一个解决方案,以确保两个文件(不)相同?

javascript reactjs file blob
1个回答
0
投票

没有办法获得真正的道路,但这并不重要。 您可以访问的是FakePath,格式为C:\fakepath\yourfilename.ext(来自input.value),如果您获得对目录的访问权限,有时会更多。 但无论如何你可能不想检查两个文件来自硬盘上的同一个地方,这没有任何重要性,因为它们很可能在第一次访问后就被修改过了。

但是,您可以想要做的是检查它们的内容是否相同。为此,您可以比较它们的字节内容:

inp1.onchange = inp2.onchange = e => {
  const file1 = inp1.files[0];
  const file2 = inp2.files[0];
  if(!file1 || !file2) return;
  compare(file1, file2)
    .then(res => console.log('are same ? ', res));
};


function compare(file1, file2) {
  // they don't have the same size, they are different
  if(file1.size !== file2.size)
    return Promise.resolve(false);
  // load both as ArrayBuffers
  return Promise.all([
    readAsArrayBuffer(file1),
    readAsArrayBuffer(file2)
  ]).then(([buf1, buf2]) => {
    // create views over our ArrayBuffers
    const arr1 = new Uint8Array(buf1);
    const arr2 = new Uint8Array(buf2);
    return !arr1.some((val, i) =>
      arr2[i] !== val // search for diffs
    );
  });
}

function readAsArrayBuffer(file) {
  // we could also have used a FileReader,
  // but Response is conveniently already Promise based
  return new Response(file).arrayBuffer();
}
<input type="file" id="inp1">
<input type="file" id="inp2">

现在,您说您不再可以访问原始文件,并且只能存储可序列化数据。在这种情况下,性能较低的解决方案是从您的文件生成哈希。 这可以在前端完成,这要归功于SubtleCrypto API,但是这个操作对于大文件来说速度很慢,你可能想要从服务器系统地做到这一点,而不是在前面做,而且只在大小的前面做它是相同的:

// a fake storage object like OP has
const store = [
  { /* an utf-8 text file whose content is `hello world`*/
    name: "helloworld.txt",
    size: 11,
    hash: "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9" // generated from server
  }
];
// the smae file as the one we fakely stored
const sameFile = new File(['hello world'], 'same-file.txt');
// a file the same size as the one we stored (needs deep check)
const sameSizeButDifferentContent = new File(['random text'], 'differentcontent.txt');


inp.onchange = e => tryToStore(inp.files[0]);

tryToStore(sameFile); // false

tryToStore(sameSizeButDifferentContent); 
// hash: "a4e082f56a58e0855a6abbf2f4ebd08895ff85ea80e634e02b210def84b557dd"


function tryToStore(file) {
  checkShouldStore(file)
    .then(result => {
      console.log('should store', file.name, result)
      if(result)
        store.push(result);
        
      // this is just for demo, in your case you would do it on the server
      if(!result.hash)
        generateHash(file).then(h => result.hash = h);
    });
}

async function checkShouldStore(file) {
  const {name, size} = file;
  const toStore = {name, size, file}; // create a wrapper object
  // first check against the sizes (fast checking)
  const sameSizes = store.filter(obj => obj.size === file.size);
  // only if some files have the same size
  if(sameSizes.length) {
    // then we generate a hash directly
    const hash = await generateHash(file);
    if(sameSizes.some(obj => obj.hash === hash)) {
      return false; // is already in our store
    }
    toStore.hash = hash; // save the hash so we don't have to generate it on server
  }
  return toStore;
}

async function generateHash(file) {
  // read as ArrayBuffer
  const buf = await new Response(file).arrayBuffer();
  // generate SHA-256 hash using crypto API
  const hash_buf = await crypto.subtle.digest("SHA-256", buf);
  // convert to Hex
  const hash_arr = [...new Uint8Array(hash_buf)]
    .map(v => v.toString(16).padStart(2, "0"));
  return hash_arr.join('');
}
<input type="file" id="inp">
© www.soinside.com 2019 - 2024. All rights reserved.