2
  1. FileReader API と onprogress イベントを使用して、HTML5 で入ってくるデータにアクセスすることは可能ですか?

  2. その場合、ファイルが完全に読み取られる前にハッシュの計算を開始できるように、MD5 またはその他の高速ハッシュ アルゴリズムの「オンライン」バージョンはありますか?

ファイルのアップロードを開始する前に、ファイル全体を送信して重複をチェックする前に、クライアント側でハッシュを計算し、ハッシュだけをサーバーに送信したいと考えています。

現時点では、古いブラウザのサポートには関心がありません。

編集:ハッシュの衝突はファイルの重複を保証しないことを認識しています。確実にする唯一の方法は、バイトごとにチェックすることです。これは、とにかくファイルをアップロードする必要があることを意味します。可能性は十分に低いので、私はこのリスクを冒しても構わないと思っています。最悪の場合、ユーザーに「このファイルは既にサーバー上にあるようです。アップロードしてもよろしいですか?」と尋ねます。

4

2 に答える 2

2

ファイルが完全に読み取られる前にハッシュの計算を開始できるように、MD5 またはその他の高速ハッシュ アルゴリズムの「オンライン」バージョンはありますか?

はい、 SHA を使用する場合はsjclを使用できます。sjcl は MD5 をネイティブでサポートしていないため、自分で作成する必要があります (他の誰かが既に行っていると思いますが)。CryptoJSは MD5 をネイティブにサポートしていますが、速度は大幅に低下しています。

ハッシュの衝突がファイルの重複を保証するものではないことは認識しています [...] 可能性は十分に低いので、このリスクを冒しても構わないと思っています。

衝突が自然に発生するよりも、流星が地球に衝突して人命を奪う可能性が高い (したがって、ハッシュの必要性が完全になくなる) 可能性は十分に低いです。もちろん、ユーザーが故意に衝突を作成しない限り、MD5 の衝突耐性が壊れているためです。

これは、あなたが達成しようとしていると私が信じていることのライブデモです。「データにアクセスする」部分は除きます。それが可能かどうかはわかりません。これはずっと前に書いたもので、CryptoJS を使用しているため、パフォーマンスはそれほど高くありませんが、仕事は完了します。重要なチャンクは次のとおりです。

function handleFileSelect(evt) 
{
    evt.stopPropagation();
    evt.preventDefault();

    var files = evt.target.files || evt.dataTransfer.files; // FileList object.

    for (var i=0, file; file = files[i]; ++i)
    {
        // this creates the FileReader and reads stuff as text
        var fr = new FileReader();

        fr.onload = (function(theFile) {
            return function (e) {
                var hashes = parsePseudoBuffer(e.target.result);

                document.getElementById('output').innerHTML += '<br />' + theFile.name + '<br />' 
                + 'MD5: ' + hashes.md5 + '<br />' + 'SHA1: ' + hashes.sha1 + '<br />' ;

            };
        }) (file);

        fr.readAsArrayBuffer(file); // ArrayBuffer
    }

}

function parsePseudoBuffer(result)
{

    var buffs = new Uint8Array(result); // buffer thingie       
    var md5 = CryptoJS.algo.MD5.create();
    var sha1 = CryptoJS.algo.SHA1.create();     
    var bufsize = 8 * 1024; // 8K buffer

    for (var bstart=0, bend=bufsize; bstart < buffs.length; bstart+=bufsize, bend+= bufsize)
    {
        var data = CryptoJS.lib.WordArray.create(buffs.subarray(bstart, bend)); 
        md5.update(data);
        sha1.update(data);          
    }

    md5 = md5.finalize(); 
    sha1 = sha1.finalize();         

    return {'md5': md5, 'sha1': sha1} ;

}
于 2013-09-19T03:44:13.880 に答える
2

私はいくつかの実験をしました。リーダー オブジェクトonprogressの incomplete を利用することで、イベント内で読み取られた最後のチャンクを取得できるようです。result( Chromereader.readAsArrayBufferのみ?) またはreader.readAsBinaryString. 文字列の問題は、そのチャンクを取得したい場合、それをスライスしてコピーを作成する必要があることです (非常に遅い)。

ArrayBuffers には、データをコピーせずにバッファにビュー.subarrayを作成するメソッドがあります。これはまさに私たちが望んでいることです。ただし、基本クラスでは使用できないようです。また、このバッファーを使用して派生クラス (例: ) を構築するとどうなるかはドキュメントからは明らかではありませんが、元のバッファーが読み取り専用プロパティを介してアクセスできることを考えると、コピーではないと想定しています。Uint8Array

sjcl と CryptoJS の両方に便利.updateなメソッドがあり、この ArrayBufferView を取り込んで、その場でハッシュを更新できます。したがって、次の解決策を思いつきました(jQuery、アンダースコア、およびsjclを使用):

$(document).on('drop', function(dropEvent) {
    dropEvent.preventDefault();

    _.each(dropEvent.originalEvent.dataTransfer.files, function(file) {
        var reader = new FileReader();
        var pos = 0;

        var hash = new sjcl.hash.sha256();

        reader.onprogress = function(progress) {
            var chunk = new Uint8Array(reader.result, pos, progress.loaded - pos);
            pos = progress.loaded;
            hash.update(chunk);
            if(progress.lengthComputable) {
                console.log((progress.loaded/progress.total*100).toFixed(1)+'%');
            }
        };

        reader.onload = function() {
            var chunk = new Uint8Array(reader.result, pos);
            if(chunk.length > 0) hash.update(chunk);
            console.log(sjcl.codec.hex.fromBits(hash.finalize()));
        };

        reader.readAsArrayBuffer(file);
    });
});

このソリューションは現在 Chrome でのみ機能し、かなり遅いことに注意してください。sjcl はファイルをハッシュするだけではなく、キーを強化していると思いますが、これは本当に私が望んでいるものではありません。後で詳しく調べます。

于 2013-09-19T06:45:45.613 に答える