我正在寻找一种方法来检查两个文件/文件(PDF,JPG,PNG)是否相同。
如果用户选择一个或多个文件,我将文件对象转换为javascript对象。我保持大小,类型,文件名,然后创建一个blob,这样我就可以将对象存储在我的redux商店中。
当用户选择另一个文件时,我想将此文件与已添加的文件进行比较(因此我可以设置相同的blobURL)。
我可以检查两个文件是否具有相同的名称,类型和大小,但是有一个更改,所有这些属性匹配,文件不一样,所以我想检查文件路径。遗憾的是,文件对象中未提供该属性。有没有办法得到这个或另一个解决方案,以确保两个文件(不)相同?
没有办法获得真正的道路,但这并不重要。
您可以访问的是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">