Javascript kullanarak sunucuya yüklemeden önce bir dosyanın MD5 karmasını hesaplamanın bir yolu var mı?
Javascript kullanarak sunucuya yüklemeden önce bir dosyanın MD5 karmasını hesaplamanın bir yolu var mı?
Yanıtlar:
MD5 algoritmasının JS uygulamaları varken , eski tarayıcılar genellikle yerel dosya sisteminden dosyaları okuyamaz .
Bunu 2009'da yazdım. Peki ya yeni tarayıcılar?
FileAPI'yi destekleyen bir tarayıcıyla , bir dosyanın içeriğini * okuyabilirsiniz - kullanıcının bir <input>
öğe veya sürükle ve bırak ile seçmiş olması gerekir . Ocak 2013 itibariyle, başlıca tarayıcıların yığılması şu şekildedir:
Büyük dosyaları verimli bir şekilde hash hale getirmek için artımlı md5 uygulayan bir kitaplık yaptım. Temel olarak bir dosyayı parçalar halinde okursunuz (hafızayı düşük tutmak için) ve aşamalı olarak hashing uyguluyorsunuz. Benioku dosyasında temel kullanım ve örnekler var.
HTML5 FileAPI'ye ihtiyacınız olduğunu unutmayın, bu yüzden kontrol ettiğinizden emin olun. Test klasöründe tam bir örnek var.
.end()
yöntemle ilgili bir sorun var . Bu yöntemi tekrar çağırırsanız, sonraki sefer yanlış sonuç verir. Çünkü dahili .end()
aramalar .reset()
. Bu bir kodlama felaketi ve kütüphane yazımı için iyi değil.
CryptoJS'nin MD5 işlevini ve HTML5 FileReader API'yi kullanarak MD5 karmasını hesaplamak oldukça kolaydır . Aşağıdaki kod parçacığı, ikili verileri nasıl okuyabileceğinizi ve Tarayıcınıza sürüklenen bir görüntüden MD5 karmasını nasıl hesaplayabileceğinizi gösterir:
var holder = document.getElementById('holder');
holder.ondragover = function() {
return false;
};
holder.ondragend = function() {
return false;
};
holder.ondrop = function(event) {
event.preventDefault();
var file = event.dataTransfer.files[0];
var reader = new FileReader();
reader.onload = function(event) {
var binary = event.target.result;
var md5 = CryptoJS.MD5(binary).toString();
console.log(md5);
};
reader.readAsBinaryString(file);
};
Sürükle ve Bırak alanını görmek için biraz CSS eklemenizi tavsiye ederim:
#holder {
border: 10px dashed #ccc;
width: 300px;
height: 300px;
}
#holder.hover {
border: 10px dashed #333;
}
Sürükle ve Bırak işlevi hakkında daha fazla bilgiyi burada bulabilirsiniz: Dosya API ve Dosya Okuyucu
Örneği Google Chrome Sürüm 32'de test ettim.
readAsBinaryString()
, standartlaştırılmamış ve Internet Explorer tarafından desteklenmiyor. Edge'de test etmedim, ancak IE11 bile desteklemiyor.
readAsBinaryString()
: caniuse.com/#feat=filereader - Microsoft Edge bunu destekliyor.
readAsBinaryString()
Eski tarayıcılar tarafından desteklenmediği için dikkatli kullanmak gerektiğini belirtmek istedim . Bulduğum bir alternatif SparkMD5. FileReader API'yi de kullanır, ancak readAsArrayBuffer
IE tarafından desteklenen yöntemi kullanır . Ve büyük dosyaları parçalar halinde okuyarak işleyebilir.
CryptoJS.lib.WordArray.create(arrayBuffer);
spark-md5
veQ
Modern bir tarayıcı (HTML5 Dosya API'sini destekleyen) kullandığınızı varsayarsak, büyük bir dosyanın MD5 Hash'ini şu şekilde hesaplayabilirsiniz (değişken parçalar üzerindeki hash'ı hesaplayacaktır)
function calculateMD5Hash(file, bufferSize) {
var def = Q.defer();
var fileReader = new FileReader();
var fileSlicer = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
var hashAlgorithm = new SparkMD5();
var totalParts = Math.ceil(file.size / bufferSize);
var currentPart = 0;
var startTime = new Date().getTime();
fileReader.onload = function(e) {
currentPart += 1;
def.notify({
currentPart: currentPart,
totalParts: totalParts
});
var buffer = e.target.result;
hashAlgorithm.appendBinary(buffer);
if (currentPart < totalParts) {
processNextPart();
return;
}
def.resolve({
hashResult: hashAlgorithm.end(),
duration: new Date().getTime() - startTime
});
};
fileReader.onerror = function(e) {
def.reject(e);
};
function processNextPart() {
var start = currentPart * bufferSize;
var end = Math.min(start + bufferSize, file.size);
fileReader.readAsBinaryString(fileSlicer.call(file, start, end));
}
processNextPart();
return def.promise;
}
function calculate() {
var input = document.getElementById('file');
if (!input.files.length) {
return;
}
var file = input.files[0];
var bufferSize = Math.pow(1024, 2) * 10; // 10MB
calculateMD5Hash(file, bufferSize).then(
function(result) {
// Success
console.log(result);
},
function(err) {
// There was an error,
},
function(progress) {
// We get notified of the progress as it is executed
console.log(progress.currentPart, 'of', progress.totalParts, 'Total bytes:', progress.currentPart * bufferSize, 'of', progress.totalParts * bufferSize);
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/q.js/1.4.1/q.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/spark-md5/2.0.2/spark-md5.min.js"></script>
<div>
<input type="file" id="file"/>
<input type="button" onclick="calculate();" value="Calculate" class="btn primary" />
</div>
FileAPI kullanmanız gerekir. En son FF ve Chrome'da mevcuttur, ancak IE9'da yoktur. Yukarıda önerilen herhangi bir md5 JS uygulamasını edinin. Bunu denedim ve bıraktım çünkü JS çok yavaştı (büyük resim dosyalarında dakikalar). Birisi MD5'i yazılan dizileri kullanarak yeniden yazarsa tekrar ziyaret edebilir.
Kod şunun gibi görünecektir:
HTML:
<input type="file" id="file-dialog" multiple="true" accept="image/*">
JS (w JQuery)
$("#file-dialog").change(function() {
handleFiles(this.files);
});
function handleFiles(files) {
for (var i=0; i<files.length; i++) {
var reader = new FileReader();
reader.onload = function() {
var md5 = binl_md5(reader.result, reader.result.length);
console.log("MD5 is " + md5);
};
reader.onerror = function() {
console.error("Could not read the file");
};
reader.readAsBinaryString(files.item(i));
}
}
reader
dosyayla çalışmadığından oldukça eminim, çünkü onload bir geri çağırmadır, değişken, onload fonksiyonları çalıştırıldığında son dosya olacaktır.
CryptoJS.lib.WordArray.create(arrayBuffer);
JS'de dosya sistemi erişimi elde etmenin imkansızlığının yanı sıra, istemci tarafından oluşturulan bir sağlama toplamına hiç güvenmem. Bu nedenle, sunucuda sağlama toplamı oluşturmak her durumda zorunludur. - Tomalak 20 Nisan '09 saat 14: 05
Çoğu durumda bu işe yaramaz. MD5'in istemci tarafında hesaplanmasını istiyorsunuz, böylece onu sunucu tarafında yeniden hesaplanan kodla karşılaştırabilir ve farklılarsa yüklemenin yanlış gittiği sonucuna varabilirsiniz. Bunu, bozulmamış dosyaları almanın önemli olduğu büyük bilimsel veri dosyalarıyla çalışan uygulamalarda yapmam gerekiyordu. Vakalarım basitti, çünkü kullanıcılar MD5'i veri analiz araçlarından zaten hesaplamıştı, bu yüzden onlara bir metin alanıyla sormam gerekiyordu.
Dosyaların karmasını elde etmek için birçok seçenek vardır. Normalde sorun, büyük dosyaların karmasını elde etmenin gerçekten yavaş olmasıdır.
Dosyanın başlangıcında 64kb ve sonunda 64kb ile dosyaların karma değerini alan küçük bir kitaplık oluşturdum.
Canlı örnek: http://marcu87.github.com/hashme/ ve kütüphane: https://github.com/marcu87/hashme
İnternette bir MD5 Hash oluşturmak için birkaç komut dosyası var.
Webtoolkit'ten biri iyidir, http://www.webtoolkit.info/javascript-md5.html
Yine de, erişim sınırlı olduğu için yerel dosya sistemine erişimi olacağına inanmıyorum.
umarım şimdiye kadar iyi bir çözüm bulmuşsunuzdur. Değilse, aşağıdaki çözüm js-spark- md5'e dayalı bir ES6 taahhüt uygulamasıdır.
import SparkMD5 from 'spark-md5';
// Read in chunks of 2MB
const CHUCK_SIZE = 2097152;
/**
* Incrementally calculate checksum of a given file based on MD5 algorithm
*/
export const checksum = (file) =>
new Promise((resolve, reject) => {
let currentChunk = 0;
const chunks = Math.ceil(file.size / CHUCK_SIZE);
const blobSlice =
File.prototype.slice ||
File.prototype.mozSlice ||
File.prototype.webkitSlice;
const spark = new SparkMD5.ArrayBuffer();
const fileReader = new FileReader();
const loadNext = () => {
const start = currentChunk * CHUCK_SIZE;
const end =
start + CHUCK_SIZE >= file.size ? file.size : start + CHUCK_SIZE;
// Selectively read the file and only store part of it in memory.
// This allows client-side applications to process huge files without the need for huge memory
fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
};
fileReader.onload = e => {
spark.append(e.target.result);
currentChunk++;
if (currentChunk < chunks) loadNext();
else resolve(spark.end());
};
fileReader.onerror = () => {
return reject('Calculating file checksum failed');
};
loadNext();
});
Aşağıdaki kod parçası, dosyayı okurken ve hashing uygularken 400 MB / s'lik bir aktarım hızını arşivleyebilen bir örnek gösterir.
WebAssembly tabanlı olan ve hash'i yalnızca js kitaplıklarından daha hızlı hesaplayan hash-wasm adlı bir kitaplık kullanıyor . 2020 itibariyle, tüm modern tarayıcılar WebAssembly'ı desteklemektedir.
const chunkSize = 64 * 1024 * 1024;
const fileReader = new FileReader();
let hasher = null;
function hashChunk(chunk) {
return new Promise((resolve, reject) => {
fileReader.onload = async(e) => {
const view = new Uint8Array(e.target.result);
hasher.update(view);
resolve();
};
fileReader.readAsArrayBuffer(chunk);
});
}
const readFile = async(file) => {
if (hasher) {
hasher.init();
} else {
hasher = await hashwasm.createMD5();
}
const chunkNumber = Math.floor(file.size / chunkSize);
for (let i = 0; i <= chunkNumber; i++) {
const chunk = file.slice(
chunkSize * i,
Math.min(chunkSize * (i + 1), file.size)
);
await hashChunk(chunk);
}
const hash = hasher.digest();
return Promise.resolve(hash);
};
const fileSelector = document.getElementById("file-input");
const resultElement = document.getElementById("result");
fileSelector.addEventListener("change", async(event) => {
const file = event.target.files[0];
resultElement.innerHTML = "Loading...";
const start = Date.now();
const hash = await readFile(file);
const end = Date.now();
const duration = end - start;
const fileSizeMB = file.size / 1024 / 1024;
const throughput = fileSizeMB / (duration / 1000);
resultElement.innerHTML = `
Hash: ${hash}<br>
Duration: ${duration} ms<br>
Throughput: ${throughput.toFixed(2)} MB/s
`;
});
<script src="https://cdn.jsdelivr.net/npm/hash-wasm"></script>
<!-- defines the global `hashwasm` variable -->
<input type="file" id="file-input">
<div id="result"></div>
Mevcut HTML5 ile bir ikili dosyanın md5 hash değerini hesaplamak mümkün olmalı, ancak bundan önceki adımın banary verisini BlobBuilder'ı String'e dönüştürmek olduğunu düşünüyorum, bu adımı yapmaya çalışıyorum: ancak başarılı olamadım.
İşte denediğim kod: Bir BlobBuilder'ı HTML5 Javascript’te dizeye dönüştürme
Bir dosya yüklemesinin içeriğine erişmek için javascript'te bir yol olduğuna inanmıyorum. Bu nedenle, bir MD5 toplamı oluşturmak için dosya içeriğine bakamazsınız.
Bununla birlikte, dosyayı sunucuya gönderebilirsiniz, bu da daha sonra bir MD5 toplamını geri gönderebilir veya dosya içeriğini geri gönderebilir .. ama bu çok iş ve muhtemelen amaçlarınız için değerli değildir.