Javascript 通过 input type = file 获取字节数组
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/32556664/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Getting byte array through input type = file
提问by stacknist
var profileImage = fileInputInByteArray;
$.ajax({
url: 'abc.com/',
type: 'POST',
dataType: 'json',
data: {
// Other data
ProfileImage: profileimage
// Other data
},
success: {
}
})
// Code in WebAPI
[HttpPost]
public HttpResponseMessage UpdateProfile([FromUri]UpdateProfileModel response) {
//...
return response;
}
public class UpdateProfileModel {
// ...
public byte[] ProfileImage {get ;set; }
// ...
}
<input type="file" id="inputFile" />
I am using ajax call to post byte[] value of a input type = file input to web api which receives in byte[] format. However, I am experiencing difficulty of getting byte array. I am expecting that we can get the byte array through File API.
我正在使用 ajax 调用将输入类型的 byte[] 值 = 文件输入发布到以 byte[] 格式接收的 web api。但是,我遇到了获取字节数组的困难。我期待我们可以通过 File API 获取字节数组。
Note: I need to store the byte array in a variable first before passing through ajax call
注意:在通过ajax调用之前,我需要先将字节数组存储在一个变量中
回答by Kaiido
[Edit]
[编辑]
As noted in comments above, while still on some UA implementations, readAsBinaryString
method didn't made its way to the specs and should not be used in production.
Instead, use readAsArrayBuffer
and loop through it's buffer
to get back the binary string :
正如上面的评论中所指出的,虽然仍然在一些 UA 实现上,但readAsBinaryString
方法并未符合规范,不应在生产中使用。相反,使用readAsArrayBuffer
并循环它是buffer
为了取回二进制字符串:
document.querySelector('input').addEventListener('change', function() {
var reader = new FileReader();
reader.onload = function() {
var arrayBuffer = this.result,
array = new Uint8Array(arrayBuffer),
binaryString = String.fromCharCode.apply(null, array);
console.log(binaryString);
}
reader.readAsArrayBuffer(this.files[0]);
}, false);
<input type="file" />
<div id="result"></div>
For a more robust way to convert your arrayBuffer in binary string, you can refer to this answer.
有关将 arrayBuffer 转换为二进制字符串的更强大的方法,您可以参考这个答案。
[old answer] (modified)
[旧答案] (修改)
Yes, the file API does provide a way to convert your File, in the <input type="file"/>
to a binary string, thanks to the FileReaderObject and its method readAsBinaryString
.
[But don't use it in production !]
是的,<input type="file"/>
由于FileReader对象及其方法,文件 API 确实提供了一种将 File 转换为二进制字符串的方法readAsBinaryString
。
[但不要在生产中使用它!]
document.querySelector('input').addEventListener('change', function(){
var reader = new FileReader();
reader.onload = function(){
var binaryString = this.result;
document.querySelector('#result').innerHTML = binaryString;
}
reader.readAsBinaryString(this.files[0]);
}, false);
<input type="file"/>
<div id="result"></div>
If you want an array buffer, then you can use the readAsArrayBuffer()
method :
如果你想要一个数组缓冲区,那么你可以使用以下readAsArrayBuffer()
方法:
document.querySelector('input').addEventListener('change', function(){
var reader = new FileReader();
reader.onload = function(){
var arrayBuffer = this.result;
console.log(arrayBuffer);
document.querySelector('#result').innerHTML = arrayBuffer + ' '+arrayBuffer.byteLength;
}
reader.readAsArrayBuffer(this.files[0]);
}, false);
<input type="file"/>
<div id="result"></div>
回答by sebu
$(document).ready(function(){
(function (document) {
var input = document.getElementById("files"),
output = document.getElementById("result"),
fileData; // We need fileData to be visible to getBuffer.
// Eventhandler for file input.
function openfile(evt) {
var files = input.files;
// Pass the file to the blob, not the input[0].
fileData = new Blob([files[0]]);
// Pass getBuffer to promise.
var promise = new Promise(getBuffer);
// Wait for promise to be resolved, or log error.
promise.then(function(data) {
// Here you can pass the bytes to another function.
output.innerHTML = data.toString();
console.log(data);
}).catch(function(err) {
console.log('Error: ',err);
});
}
/*
Create a function which will be passed to the promise
and resolve it when FileReader has finished loading the file.
*/
function getBuffer(resolve) {
var reader = new FileReader();
reader.readAsArrayBuffer(fileData);
reader.onload = function() {
var arrayBuffer = reader.result
var bytes = new Uint8Array(arrayBuffer);
resolve(bytes);
}
}
// Eventlistener for file input.
input.addEventListener('change', openfile, false);
}(document));
});
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<input type="file" id="files"/>
<div id="result"></div>
</body>
</html>
回答by vapcguy
This is a long post, but I was tired of all these examples that weren't working for me because they used Promise objects or an errant this
that has a different meaning when you are using Reactjs. My implementation was using a DropZone with reactjs, and I got the bytes using a framework similar to what is posted at this following site, when nothing else above would work: https://www.mokuji.me/article/drop-upload-tutorial-1. There were 2 keys, for me:
这是一篇很长的文章,但我厌倦了所有这些对我不起作用的示例,因为它们使用了 Promise 对象或this
在使用 Reactjs 时具有不同含义的错误。我的实现是使用带有 reactjs 的 DropZone,并且我使用类似于以下站点上发布的框架获取字节,但上面的其他内容都不起作用:https: //www.mokuji.me/article/drop-upload-教程-1。对我来说,有两个键:
- You have to get the bytes from the event object, using and during a FileReader's onload function.
I tried various combinations, but in the end, what worked was:
const bytes = e.target.result.split('base64,')[1];
- 您必须使用 FileReader 的 onload 函数并在其期间从事件对象中获取字节。
我尝试了各种组合,但最终,有效的是:
const bytes = e.target.result.split('base64,')[1];
Where e
is the event. React requires const
, you could use var
in plain Javascript. But that gave me the base64 encoded byte string.
活动在哪里e
。React 需要const
,您可以var
在纯 Javascript 中使用。但这给了我 base64 编码的字节字符串。
So I'm just going to include the applicable lines for integrating this as if you were using React, because that's how I was building it, but try to also generalize this, and add comments where necessary, to make it applicable to a vanilla Javascript implementation - caveated that I did not use it like that in such a construct to test it.
因此,我将像使用 React 一样包含用于集成它的适用行,因为这就是我构建它的方式,但也尝试对此进行概括,并在必要时添加注释,使其适用于 vanilla Javascript实现 - 警告说我没有在这样的构造中使用它来测试它。
These would be your bindings at the top, in your constructor, in a React framework (not relevant to a vanilla Javascript implementation):
这些将是顶部、构造函数、React 框架中的绑定(与普通 Javascript 实现无关):
this.uploadFile = this.uploadFile.bind(this);
this.processFile = this.processFile.bind(this);
this.errorHandler = this.errorHandler.bind(this);
this.progressHandler = this.progressHandler.bind(this);
And you'd have onDrop={this.uploadFile}
in your DropZone element. If you were doing this without React, this is the equivalent of adding the onclick event handler you want to run when you click the "Upload File" button.
您将拥有onDrop={this.uploadFile}
DropZone 元素。如果您在没有 React 的情况下执行此操作,这相当于添加您要在单击“上传文件”按钮时运行的 onclick 事件处理程序。
<button onclick="uploadFile(event);" value="Upload File" />
Then the function (applicable lines... I'll leave out my resetting my upload progress indicator, etc.):
然后是功能(适用的行......我将省略我重置上传进度指示器等):
uploadFile(event){
// This is for React, only
this.setState({
files: event,
});
console.log('File count: ' + this.state.files.length);
// You might check that the "event" has a file & assign it like this
// in vanilla Javascript:
// var files = event.target.files;
// if (!files && files.length > 0)
// files = (event.dataTransfer ? event.dataTransfer.files :
// event.originalEvent.dataTransfer.files);
// You cannot use "files" as a variable in React, however:
const in_files = this.state.files;
// iterate, if files length > 0
if (in_files.length > 0) {
for (let i = 0; i < in_files.length; i++) {
// use this, instead, for vanilla JS:
// for (var i = 0; i < files.length; i++) {
const a = i + 1;
console.log('in loop, pass: ' + a);
const f = in_files[i]; // or just files[i] in vanilla JS
const reader = new FileReader();
reader.onerror = this.errorHandler;
reader.onprogress = this.progressHandler;
reader.onload = this.processFile(f);
reader.readAsDataURL(f);
}
}
}
There was this question on that syntax, for vanilla JS, on how to get that file object:
对于vanilla JS,关于如何获取该文件对象的语法有一个问题:
JavaScript/HTML5/jQuery 拖放上传 - “未捕获的类型错误:无法读取未定义的属性‘文件’”
Note that React's DropZone will already put the File object into this.state.files
for you, as long as you add files: [],
to your this.state = { .... }
in your constructor. I added syntax from an answer on that post on how to get your File object. It should work, or there are other posts there that can help. But all that Q/A told me was how to get the File
object, not the blob data, itself. And even if I did fileData = new Blob([files[0]]);
like in sebu's answer, which didn't include var
with it for some reason, it didn't tell me how to read that blob's contents, and how to do it without a Promise object. So that's where the FileReader came in, though I actually tried and found I couldn't use their readAsArrayBuffer
to any avail.
请注意,React 的 DropZone 已经this.state.files
为您放入 File 对象,只要files: [],
您this.state = { .... }
在构造函数中添加到您的。我从那篇关于如何获取 File 对象的帖子的答案中添加了语法。它应该有效,或者那里有其他帖子可以提供帮助。但 Q/A 告诉我的只是如何获取File
对象,而不是 blob 数据本身。即使我确实fileData = new Blob([files[0]]);
喜欢 sebu 的回答,var
但由于某种原因没有包含在其中,它也没有告诉我如何阅读该 blob 的内容,以及如何在没有 Promise 对象的情况下进行阅读。所以这就是 FileReader 的readAsArrayBuffer
用武之地,尽管我实际上尝试过并发现我无法使用它们。
You will have to have the other functions that go along with this construct - one to handle onerror
, one for onprogress
(both shown farther below), and then the main one, onload
, that actually does the work once a method on reader
is invoked in that last line. Basically you are passing your event.dataTransfer.files[0]
straight into that onload
function, from what I can tell.
您将必须拥有与此构造一起使用的其他功能 - 一个用于处理onerror
,一个用于onprogress
(两者都在下面显示),然后是主要的,onload
一旦reader
在最后一行调用方法 on ,它就会实际完成工作. 基本上,据我所知,您将event.dataTransfer.files[0]
直接传递给该onload
函数。
So the onload
method calls my processFile()
function (applicable lines, only):
所以该onload
方法调用我的processFile()
函数(仅适用行):
processFile(theFile) {
return function(e) {
const bytes = e.target.result.split('base64,')[1];
}
}
And bytes
should have the base64 bytes.
并且bytes
应该有 base64 字节。
Additional functions:
附加功能:
errorHandler(e){
switch (e.target.error.code) {
case e.target.error.NOT_FOUND_ERR:
alert('File not found.');
break;
case e.target.error.NOT_READABLE_ERR:
alert('File is not readable.');
break;
case e.target.error.ABORT_ERR:
break; // no operation
default:
alert('An error occurred reading this file.');
break;
}
}
progressHandler(e) {
if (e.lengthComputable){
const loaded = Math.round((e.loaded / e.total) * 100);
let zeros = '';
// Percent loaded in string
if (loaded >= 0 && loaded < 10) {
zeros = '00';
}
else if (loaded < 100) {
zeros = '0';
}
// Display progress in 3-digits and increase bar length
document.getElementById("progress").textContent = zeros + loaded.toString();
document.getElementById("progressBar").style.width = loaded + '%';
}
}
And applicable progress indicator markup:
以及适用的进度指示器标记:
<table id="tblProgress">
<tbody>
<tr>
<td><b><span id="progress">000</span>%</b> <span className="progressBar"><span id="progressBar" /></span></td>
</tr>
</tbody>
</table>
And CSS:
和 CSS:
.progressBar {
background-color: rgba(255, 255, 255, .1);
width: 100%;
height: 26px;
}
#progressBar {
background-color: rgba(87, 184, 208, .5);
content: '';
width: 0;
height: 26px;
}
EPILOGUE:
结语:
Inside processFile()
, for some reason, I couldn't add bytes
to a variable I carved out in this.state
. So, instead, I set it directly to the variable, attachments
, that was in my JSON object, RequestForm
- the same object as my this.state
was using. attachments
is an array so I could push multiple files. It went like this:
在内部processFile()
,出于某种原因,我无法添加bytes
到我在this.state
. 因此,相反,我将它直接设置为attachments
JSON 对象中的变量RequestForm
- 与我this.state
使用的对象相同。 attachments
是一个数组,所以我可以推送多个文件。它是这样的:
const fileArray = [];
// Collect any existing attachments
if (RequestForm.state.attachments.length > 0) {
for (let i=0; i < RequestForm.state.attachments.length; i++) {
fileArray.push(RequestForm.state.attachments[i]);
}
}
// Add the new one to this.state
fileArray.push(bytes);
// Update the state
RequestForm.setState({
attachments: fileArray,
});
Then, because this.state
already contained RequestForm
:
然后,因为this.state
已经包含RequestForm
:
this.stores = [
RequestForm,
]
I could reference it as this.state.attachments
from there on out. React feature that isn't applicable in vanilla JS. You could build a similar construct in plain JavaScript with a global variable, and push, accordingly, however, much easier:
我可以从那时起引用它this.state.attachments
。不适用于 vanilla JS 的 React 功能。您可以使用全局变量在纯 JavaScript 中构建类似的结构,然后相应地推送,但要容易得多:
var fileArray = new Array(); // place at the top, before any functions
// Within your processFile():
var newFileArray = [];
if (fileArray.length > 0) {
for (var i=0; i < fileArray.length; i++) {
newFileArray.push(fileArray[i]);
}
}
// Add the new one
newFileArray.push(bytes);
// Now update the global variable
fileArray = newFileArray;
Then you always just reference fileArray
, enumerate it for any file byte strings, e.g. var myBytes = fileArray[0];
for the first file.
然后你总是只引用fileArray
,为任何文件字节字符串枚举它,例如var myBytes = fileArray[0];
第一个文件。
回答by V?n Minh Nguyên
This is simple way to convert files to Base64 and avoid "maximum call stack size exceeded at FileReader.reader.onload" with the file has big size.
这是将文件转换为 Base64 并避免“在 FileReader.reader.onload 时超出最大调用堆栈大小”的简单方法,因为文件很大。
document.querySelector('#fileInput').addEventListener('change', function () {
var reader = new FileReader();
var selectedFile = this.files[0];
reader.onload = function () {
var comma = this.result.indexOf(',');
var base64 = this.result.substr(comma + 1);
console.log(base64);
}
reader.readAsDataURL(selectedFile);
}, false);
<input id="fileInput" type="file" />
回答by Dinesh
document.querySelector('input').addEventListener('change', function(){
var reader = new FileReader();
reader.onload = function(){
var arrayBuffer = this.result,
array = new Uint8Array(arrayBuffer),
binaryString = String.fromCharCode.apply(null, array);
console.log(binaryString);
console.log(arrayBuffer);
document.querySelector('#result').innerHTML = arrayBuffer + ' '+arrayBuffer.byteLength;
}
reader.readAsArrayBuffer(this.files[0]);
}, false);
<input type="file"/>
<div id="result"></div>