通过 input type = file 获取字节数组

IT技术 javascript html fileapi
2021-02-08 20:51:12

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" />

我正在使用 ajax 调用将输入类型的 byte[] 值 = 文件输入发布到以 byte[] 格式接收的 web api。但是,我遇到了获取字节数组的困难。我期待我们可以通过 File API 获取字节数组。

注意:在通过ajax调用之前,我需要先将字节数组存储在一个变量中

6个回答

[编辑]

正如上面的评论中所指出的,虽然仍然在一些 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>

有关将 arrayBuffer 转换为二进制字符串的更强大的方法,您可以参考这个答案


[旧答案] (修改)

是的,<input type="file"/>由于FileReader对象及其方法,文件 API 确实提供了一种将 File 转换为二进制字符串的方法readAsBinaryString
[但不要在生产中使用它!]

如果你想要一个数组缓冲区,那么你可以使用以下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>

看来web api接受的请求是空的。但是,我得到了长度显示。如何以警报形式输出 arraybuffer 的显示?
2021-03-20 20:51:12
我如何分配 readAsArrayBuffer 结果并通过 ajax 调用?
2021-03-22 20:51:12
arrayBuffer.byteLength它在我给你的代码片段中
2021-03-25 20:51:12
这不支持 ie 并且已弃用。我建议切换到上述方法:readAsArrayBuffer
2021-03-28 20:51:12
在读取器加载事件中,使用 arraybuffer 作为参数调用 ajax 函数(reader.onload = function(){ yourAwesomeAjaxFunction(this.result)};然后function yourAwesomeAjaxFunction( profileImage ){ $.ajax({ url: 'abc.com/',...
2021-04-12 20:51:12

$(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>

喜西武..感谢这个答案..这似乎为我工作..但是,其中arrayBufferbytes是字节数组?如果bytes是(根据名称),那么是什么arrayBuffer@vapcguy,如果你也能在这里帮助我,我将不胜感激..
2021-03-18 20:51:12
请注意,我相信 11 之前的 IE 版本不支持使用Promise对象。
2021-03-28 20:51:12
@RaiarrayBuffer可能是你想要的那个,但是当我看到这个的时候,我也想不通,我用我自己的方式reader.readAsDataURL(f);(而不是reader.readAsArrayBuffer(fileData);)我使用的地方var f = files[0];,这与fileData上面的类似,除了我没有不要把它放在Blob第一个。我也提供了关于这个问题的答案,它最初是为 React 编写的,但仔细阅读它也会给你一个普通的 JS 版本。
2021-04-08 20:51:12

这是一篇很长的文章,但我厌倦了所有这些对我不起作用的示例,因为它们使用了 Promise 对象或this在使用 Reactjs 时具有不同含义的错误我的实现是使用带有 reactjs 的 DropZone,并且我使用类似于以下站点上发布的框架获取字节,但上面的其他内容都不起作用:https : //www.mokuji.me/article/drop-upload-教程-1对我来说,有 2 个键:

  1. 您必须使用 FileReader 的 onload 函数并在其期间从事件对象中获取字节。
  2. 我尝试了各种组合,但最终,有效的是:

    const bytes = e.target.result.split('base64,')[1];

活动在哪里eReact 需要const,您可以var在纯 Javascript 中使用但这给了我 base64 编码的字节字符串。

因此,我将像使用 React 一样包含用于集成它的适用行,因为这就是我构建它的方式,但也尝试对此进行概括,并在必要时添加注释,使其适用于 vanilla Javascript实现 - 警告说我没有在这样的构造中使用它来测试它。

这些将是顶部、构造函数、React 框架中的绑定(与 vanilla 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);

您将拥有onDrop={this.uploadFile}DropZone 元素。如果您在没有 React 的情况下执行此操作,这相当于添加您要在单击“上传文件”按钮时运行的 onclick 事件处理程序。

<button onclick="uploadFile(event);" value="Upload File" />

然后是功能(适用的行......我将省略我重置上传进度指示器等):

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);
      }      
   }
}

对于vanilla JS,关于如何获取该文件对象的语法有一个问题:

JavaScript/HTML5/jQuery 拖放上传 - “未捕获的类型错误:无法读取未定义的属性‘文件’”

请注意,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用武之地,尽管我实际上尝试过并发现我无法使用它们

您将必须拥有与此构造一起使用的其他函数 - 一个用于处理onerror,一个用于onprogress(两者都在下面显示),然后是主要的,onload一旦reader在最后一行调用方法 on ,它就会实际完成工作. 基本上,据我所知,您将event.dataTransfer.files[0]直接传递给onload函数。

所以该onload方法调用我的processFile()函数(仅适用行):

processFile(theFile) {
  return function(e) {
    const bytes = e.target.result.split('base64,')[1];
  }
}

并且bytes应该有 base64 字节。

附加功能:

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 + '%';
    }
  }

和适用的进度指示器标记​​:

<table id="tblProgress">
  <tbody>
    <tr>
      <td><b><span id="progress">000</span>%</b> <span className="progressBar"><span id="progressBar" /></span></td>
    </tr>                    
  </tbody>
</table>

和 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;
}

结语:

在内部processFile(),出于某种原因,我无法添加bytes到我在this.state. 因此,相反,我将其直接设置为attachmentsJSON 对象中的变量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,
  });

然后,因为this.state已经包含RequestForm

this.stores = [
  RequestForm,    
]

我可以从那时起引用它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;

然后你总是只是引用fileArray,为任何文件字节字符串枚举它,例如var myBytes = fileArray[0];第一个文件。

这是将文件转换为 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" />

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>