Câu trả lời:
Mặc dù có triển khai JS của thuật toán MD5, nhưng các trình duyệt cũ hơn thường không thể đọc tệp từ hệ thống tệp cục bộ .
Tôi đã viết điều đó vào năm 2009. Vậy còn các trình duyệt mới thì sao?
Với trình duyệt hỗ trợ FileAPI , bạn * có thể * đọc nội dung của tệp - người dùng phải chọn tệp đó, bằng một <input>
phần tử hoặc kéo và thả. Kể từ tháng 1 năm 2013, đây là cách các trình duyệt chính xếp chồng lên nhau:
Tôi đã tạo một thư viện triển khai md5 gia tăng để băm các tệp lớn một cách hiệu quả. Về cơ bản, bạn đọc một tệp theo từng phần (để giữ cho bộ nhớ thấp) và băm nó tăng dần. Bạn có cách sử dụng cơ bản và các ví dụ trong readme.
Hãy lưu ý rằng bạn cần HTML5 FileAPI, vì vậy hãy nhớ kiểm tra nó. Có một ví dụ đầy đủ trong thư mục thử nghiệm.
.end()
phương pháp này. Nếu bạn gọi lại phương thức này thì lần sau nó sẽ cho kết quả sai. Vì .end()
gọi .reset()
nội bộ. Đây là một thảm họa mã hóa và không tốt cho việc viết thư viện.
khá dễ dàng để tính toán băm MD5 bằng cách sử dụng hàm MD5 của CryptoJS và API HTML5 FileReader . Đoạn mã sau cho biết cách bạn có thể đọc dữ liệu nhị phân và tính toán băm MD5 từ một hình ảnh đã được kéo vào Trình duyệt của bạn:
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);
};
Tôi khuyên bạn nên thêm một số CSS để xem khu vực Kéo & Thả:
#holder {
border: 10px dashed #ccc;
width: 300px;
height: 300px;
}
#holder.hover {
border: 10px dashed #333;
}
Bạn có thể tìm thêm thông tin về chức năng Kéo & Thả tại đây: File API & FileReader
Tôi đã thử nghiệm mẫu trong Google Chrome Phiên bản 32.
readAsBinaryString()
chưa được chuẩn hóa và không được hỗ trợ bởi Internet Explorer. Tôi đã không thử nghiệm nó trong Edge, nhưng ngay cả IE11 cũng không hỗ trợ nó.
readAsBinaryString()
: caniuse.com/#feat=filereader - Microsoft Edge hỗ trợ nó.
readAsBinaryString()
khi sử dụng vì nó không được các trình duyệt cũ hỗ trợ. Một giải pháp thay thế tôi đã tìm thấy là SparkMD5. Nó cũng sử dụng API FileReader nhưng là phương pháp readAsArrayBuffer
được IE hỗ trợ. Và nó có thể xử lý các tập tin lớn bằng cách đọc chúng thành từng phần.
CryptoJS.lib.WordArray.create(arrayBuffer);
spark-md5
vàQ
Giả sử bạn đang sử dụng một trình duyệt hiện đại (hỗ trợ API tệp HTML5), đây là cách bạn tính toán băm MD5 của một tệp lớn (nó sẽ tính toán băm trên các phần biến)
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>
Bạn cần sử dụng FileAPI. Nó có sẵn trong FF & Chrome mới nhất, nhưng không có sẵn trong IE9. Lấy bất kỳ triển khai JS md5 nào được đề xuất ở trên. Tôi đã thử điều này và bỏ nó vì JS quá chậm (vài phút trên các tệp hình ảnh lớn). Có thể truy cập lại nếu ai đó viết lại MD5 bằng cách sử dụng mảng đã nhập.
Mã sẽ trông giống như sau:
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
biến sẽ là tệp cuối cùng vào thời điểm các hàm onload được chạy.
CryptoJS.lib.WordArray.create(arrayBuffer);
Ngoài việc không thể có quyền truy cập hệ thống tệp trong JS, tôi sẽ không đặt bất kỳ sự tin tưởng nào vào tổng kiểm tra do máy khách tạo. Vì vậy, việc tạo tổng kiểm tra trên máy chủ là bắt buộc trong mọi trường hợp. - Tomalak 20 tháng 4 '09 lúc 14:05
Điều đó là vô ích trong hầu hết các trường hợp. Bạn muốn MD5 được tính toán ở phía máy khách, để bạn có thể so sánh nó với mã được biên dịch lại ở phía máy chủ và kết luận quá trình tải lên đã bị lỗi nếu chúng khác nhau. Tôi cần phải làm điều đó trong các ứng dụng làm việc với các tệp dữ liệu khoa học lớn, nơi việc nhận các tệp không bị gián đoạn là chìa khóa. Trường hợp của tôi rất đơn giản, vì người dùng đã tính toán MD5 từ các công cụ phân tích dữ liệu của họ, vì vậy tôi chỉ cần hỏi họ bằng một trường văn bản.
Để lấy băm tệp, có rất nhiều tùy chọn. Thông thường, vấn đề là việc lấy băm của các tệp lớn thực sự rất chậm.
Tôi đã tạo một thư viện nhỏ để lấy băm của tệp, với 64kb ở đầu tệp và 64kb ở cuối tệp.
Ví dụ trực tiếp: http://marcu87.github.com/hashme/ và thư viện: https://github.com/marcu87/hashme
Có một vài script trên internet để tạo MD5 Hash.
Một từ webtoolkit là tốt, http://www.webtoolkit.info/javascript-md5.html
Mặc dù, tôi không tin rằng nó sẽ có quyền truy cập vào hệ thống tệp cục bộ vì quyền truy cập đó bị giới hạn.
hy vọng bạn đã tìm thấy một giải pháp tốt bây giờ. Nếu không, giải pháp bên dưới là triển khai lời hứa ES6 dựa trên js-spark-md5
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();
});
Đoạn mã sau đây cho thấy một ví dụ, có thể lưu trữ thông lượng 400 MB / s trong khi đọc và băm tệp.
Nó đang sử dụng một thư viện có tên là hash-wasm , dựa trên WebAssembly và tính toán băm nhanh hơn so với các thư viện chỉ js. Kể từ năm 2020, tất cả các trình duyệt hiện đại đều hỗ trợ WebAssembly.
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>
Với HTML5 hiện tại, có thể tính toán băm md5 của một tệp nhị phân, Nhưng tôi nghĩ rằng bước trước đó sẽ là chuyển đổi dữ liệu banary BlobBuilder thành một chuỗi, tôi đang cố gắng thực hiện bước này: nhưng không thành công.
Đây là mã tôi đã thử: Chuyển đổi BlobBuilder thành chuỗi, trong HTML5 Javascript
Tôi không tin rằng có cách trong javascript để truy cập nội dung của tệp tải lên. Vì vậy, bạn không thể xem nội dung tệp để tạo tổng MD5.
Tuy nhiên, bạn có thể gửi tệp đến máy chủ, sau đó có thể gửi lại tổng MD5 hoặc gửi lại nội dung tệp .. nhưng đó là rất nhiều công việc và có lẽ không đáng giá cho mục đích của bạn.