Javascript: сравнить два выбранных файла по пути к файлу - PullRequest
0 голосов
/ 18 марта 2019

Я ищу способ проверить, совпадают ли два файла / документа (PDF, JPG, PNG).

Если пользователь выбирает один или несколько файлов, я преобразую объект файла в объект JavaScript. Я сохраняю размер, тип, имя файла и создаю большой двоичный объект, чтобы я мог сохранить объект в своем хранилище редуксов.

Когда пользователь выбирает другой файл, я хочу сравнить этот файл с файлами, которые уже были добавлены (поэтому я могу установить тот же blobURL).

Я могу проверить, имеют ли два файла одинаковое имя, тип и размер, но есть изменение, которое соответствует всем этим свойствам, и файлы не совпадают, поэтому я хотел бы проверить путь к файлу. К сожалению, это свойство не предоставляется в объекте File. Есть ли способ получить это или другое решение, чтобы убедиться, что оба файла (не) одинаковы?

1 Ответ

0 голосов
/ 18 марта 2019

Нет, нет способа получить реальный путь, но это не имеет значения.
Все, к чему у вас есть доступ, - это 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">
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...