我意识到当文件大于200MB(类似的东西)时,新的Mozilla Firefox会返回allocation size overflow
(在FileReader.ReadAsBinaryString()上)。
以下是我测试客户端网络浏览器的一些代码:
function upload(fileInputId, fileIndex)
{
var file = document.getElementById(fileInputId).files[fileIndex];
var blob;
var reader = new FileReader();
reader.readAsBinaryString(file);
reader.onloadend = function(evt)
{
xhr = new XMLHttpRequest();
xhr.open("POST", "upload.php", true);
XMLHttpRequest.prototype.mySendAsBinary = function(text){
var data = new ArrayBuffer(text.length);
var ui8a = new Uint8Array(data, 0);
for (var i = 0; i < text.length; i++){
ui8a[i] = (text.charCodeAt(i) & 0xff);
}
if(typeof window.Blob == "function")
{
blob = new Blob([data]);
}else{
var bb = new (window.MozBlobBuilder || window.WebKitBlobBuilder || window.BlobBuilder)();
bb.append(data);
blob = bb.getBlob();
}
this.send(blob);
}
var eventSource = xhr.upload || xhr;
eventSource.addEventListener("progress", function(e) {
var position = e.position || e.loaded;
var total = e.totalSize || e.total;
var percentage = Math.round((position/total)*100);
});
xhr.onreadystatechange = function()
{
if(xhr.readyState == 4)
{
if(xhr.status == 200)
{
console.log("Done");
}else{
console.log("Fail");
}
}
};
xhr.mySendAsBinary(evt.target.result);
};
}
所以我尝试将其更改为FileReader.ReadAsArrayBuffer(),错误未显示但数据不相同(因为它不是二进制字符串读取)。
有没有人有任何解决方案来解决这个问题?除了FileReader实现之外,有没有什么办法可以在原始/字符串中将更大的文件从JS上传到Web服务器?
我在Mozilla JS文档中读过:
此功能不符合标准,不符合标准。不要 在面向Web的生产站点上使用它:它不会适用于所有人 用户。两者之间可能存在很大的不兼容性 实现和行为可能在未来发生变化。 - Mozilla
如果不是ReadAsBinaryString,那么如何实现ReadAsArrayBuffer或ReadAsText
答案 0 :(得分:0)
要将文件发送到网络服务器,您根本不需要js。单独使用HTML就可以使用<form>
元素完成此操作。
现在,如果您想要浏览js,例如捕获不同的ProgressEvents,那么您可以直接发送您的文件,无需随意阅读。
为此,您有两个(或三个)解决方案。
如果您的服务器能够处理PUT requests,您只需xhr.send(file);
。
否则,您必须通过FormData。
// if you really want to go the XHR way
document.forms[0].onsubmit = function handleSubmit(evt) {
if(!window.FormData) { // old browser use the <form>
return;
}
// now we handle the submit through js
evt.preventDefault();
var fD = new FormData(this);
var xhr = new XMLHttpRequest();
xhr.onprogress = function handleProgress(evt){};
xhr.onload = function handleLoad(evt){};
xhr.onerror = function handleError(evt){};
xhr.open(this.method, this.action);
// xhr.send(fD); // won't work in StackSnippet
log(fD, this.method, this.action); // so we just log its content
};
function log(formData, method, action) {
console.log('would have sent');
for(let [key, val] of formData.entries())
console.log(key, val);
console.log('through', method);
console.log('to', action);
}
<!-- this in itself is enough -->
<form method="POST" action="your_server.page">
<input type="file" name="file_upload">
<input type="submit">
</form>
现在,您发送了一条评论,说您无法将大于1GB的文件上传到您的服务器。 这个限制只是由于您的服务器配置,所以如果您想接受这样大的文件,最好是正确配置它。
但是如果你真的想要通过块发送文件,那么即使这样也不要离开Blob界面。
确实Blob有一个slice()
方法,所以使用它。
document.forms[0].onsubmit = function handleSubmit(evt) {
evt.preventDefault();
var file = this.elements[0].files[0];
var processed = 0;
if(file) {
// var MAX_CHUNK_SIZE = Math.min(file.size, server_max_size);
// for demo we just split in 10 chunks
var MAX_CHUNK_SIZE = file.size > 10 ? (file.size / 10) | 0 : 1;
loadChunk(0);
}
function loadChunk(start) {
var fD = new FormData();
var sliced = file.slice(start, start+MAX_CHUNK_SIZE);
processed += sliced.size; // only for demo
fD.append('file_upload', sliced, file.name);
fD.append('starting_index', start);
if(start + MAX_CHUNK_SIZE >= file.size) {
fD.append('last_chunk', true);
}
var xhr = new XMLHttpRequest();
xhr.open('POST', 'your_server.page');
xhr.onload = function onchunkposted(evt) {
if(start + MAX_CHUNK_SIZE >= file.size) {
console.log('All done. Original file size: %s, total of chunks sizes %s', file.size, processed);
return;
}
loadChunk(start + MAX_CHUNK_SIZE);
};
// xhr.send(fD);
log(fD);
setTimeout(xhr.onload, 200); // fake XHR onload
}
};
function log(formData, method, action) {
console.log('would have sent');
for(let [key, val] of formData.entries())
console.log(key, val);
}
<form method="POST" action="your_server.page">
<input type="file" name="file_upload">
<input type="submit">
</form>
但是你绝对不需要通过FileReader进行此操作。
<子> 实际上,在这里使用FileReader有意义的唯一情况是某些Android浏览器不支持将Blob传递给FormData,即使它们没有提供任何关于它的线索。 因此,在这种情况下,您必须设置服务器以通知您请求为空,然后仅将文件读取为您将代替原始文件发送的dataURI。 子>
答案 1 :(得分:-1)
答案 2 :(得分:-1)
Kaiido声明是正确的
要将文件发送到网络服务器,您根本不需要js
但这并不能回答我的问题。使用简单XMLHttpRequest()
可以上传文件并跟踪这些进度。但是,它还不是。来自<form>
或使用XMLHttpRequest()
的直接上传需要在php设置中增加upload limit
。这种方法对我来说不方便。如果客户端上传文件为4GB?所以我需要增加到4GB。然后下次,客户端上传文件为6GB,那么我必须增加到6GB。
使用slice()
方法对于更大的文件是有意义的,因为我们可以将它逐个发送到服务器。但是这次我还没有使用它。
我的部分测试按照我的意愿进行。如果我错了,我希望有些专家可以纠正我。 我的Upload.js
function upload(fileInputId, fileIndex)
{
var file = document.getElementById(fileInputId).files[fileIndex];
var blob;
var reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.onloadend = function(evt)
{
xhr = new XMLHttpRequest();
xhr.open("POST", "upload.php?name=" + base64_encode(file.name), true);
XMLHttpRequest.prototype.mySendAsBinary = function(text){
var ui8a = new Uint8Array(new Int8Array(text));
if(typeof window.Blob == "function")
{
blob = new Blob([ui8a]);
}else{
var bb = new (window.MozBlobBuilder || window.WebKitBlobBuilder || window.BlobBuilder)();
bb.append(ui8a);
blob = bb.getBlob();
}
this.send(blob);
}
var eventSource = xhr.upload || xhr;
eventSource.addEventListener("progress", function(e) {
var position = e.position || e.loaded;
var total = e.totalSize || e.total;
var percentage = Math.round((position/total)*100);
console.log(percentage);
});
xhr.onreadystatechange = function()
{
if(xhr.readyState == 4)
{
if(xhr.status == 200)
{
console.log("Done");
}else{
console.log("Fail");
}
}
};
xhr.mySendAsBinary(evt.target.result);
};
}
以下是PHP服务器如何从JS </ p>收听ArrayBuffer
if(isset($_GET["name"])){
$name = base64_decode($_GET["name"]);
$loc = $name;
$inputHandler = fopen('php://input', "r");
$fileHandler = fopen($loc, "w+");
while(true) {
//$buffer = fgets($inputHandler, 1024);
$buffer = fread($inputHandler, 1000000);
if (strlen($buffer) == 0) {
fclose($inputHandler);
fclose($fileHandler);
return true;
}
//$b = base64_encode($buffer);
fwrite($fileHandler, $buffer);
}
}
上述方法效果很好。 FileReader将文件读取为ArrayBuffer
上传到服务器。对我来说,从ReadAsBinaryString()
迁移到ReadAsArrayBuffer()
非常重要,ReadAsArrayBuffer()
有一些更好的表现,而不是ReadAsBinaryString()
以上是某些原因,为什么有些开发人员依赖FileReader
API:
ArrayBuffer
发送时,开发人员可以在上传过程中轻松Encrypt
该文件。此方法还支持上传任何类型的文件。我做了一些测试,我发现ReadAsArrayBuffer()
方法比ReadAsBinaryString()
更快并直接表单上传。你可以尝试一下。
安全通知
以上代码仅在测试代码下,要在生产中使用,您必须考虑在HTTPS下以GET
或POST
发送数据。