我正在寻找一种方法来检查两个文件/文档(PDF,JPG,PNG)是否相同。
如果用户选择一个或多个文件,我会将文件对象转换为javascript对象。我保留了大小,类型,文件名,并创建了一个Blob,以便可以将该对象存储在我的redux存储中。
当用户选择另一个文件时,我想将此文件与已经添加的文件进行比较(因此我可以设置相同的blobURL)。
我可以检查两个文件的名称,类型和大小是否相同,但是所有这些属性都匹配并且文件不相同,因此我想检查文件路径。不幸的是,文件对象中没有提供该属性。是否有办法获得此解决方案或其他解决方案,以确保两个文件都不相同?
答案 0 :(得分:0)
没有办法获得真实的路径,但这没关系。
您只能访问C:\fakepath\yourfilename.ext
形式的FakePath(来自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">