如何在JS中从ArrayBuffer写入文件

32

我正在尝试为Meteor框架编写文件上传器。 其原理是在客户端将文件从ArrayBuffer中分割成小的4096位数据包,然后通过Meteor.method发送到服务器。

下面是客户端发送数据块到服务器的简化代码,直到offset达到data.byteLength为止:

// data is an ArrayBuffer
var total = data.byteLength;
var offset = 0;

var upload = function() {
  var length = 4096; // chunk size

  // adjust the last chunk size
  if (offset + length > total) {
     length = total - offset;
  }

  // I am using Uint8Array to create the chunk
  // because it can be passed to the Meteor.method natively
  var chunk = new Uint8Array(data, offset, length);

  if (offset < total) {
     // Send the chunk to the server and tell it what file to append to
     Meteor.call('uploadFileData', fileId, chunk, function (err, length) {
        if (!err) {
          offset += length;
          upload();
        }
     }
  }
};
upload(); // start uploading

以下是服务器端接收数据块并将其写入文件系统的简化代码:

var fs = Npm.require('fs');
var Future = Npm.require('fibers/future');

Meteor.methods({
  uploadFileData: function(fileId, chunk) {
    var fut = new Future();
    var path = '/uploads/' + fileId;

    // I tried that with no success
    chunk = String.fromCharCode.apply(null, chunk);

    // how to write the chunk that is an Uint8Array to the disk ?
    fs.appendFile(path, chunk, 'binary', function (err) {
      if (err) {
        fut.throw(err);
      } else {
        fut.return(chunk.length);
      }
    });
    return fut.wait();
  }
});

我无法成功地将一个有效的文件写入磁盘,实际上文件已经保存,但我无法打开它,当我在文本编辑器中查看其内容时,它与原始文件(例如JPG)类似,但有些字符不同,我认为这可能是编码问题,因为文件大小不一样,但我不知道如何解决...


1
你能传递一个 Blob 吗?它们通常在 Node 中作为缓冲区出现,而 fs.AppendFile() 可以处理。 - dandavis
1
@dandavis,实际上你给了我一半的答案,看来解决方案毕竟很简单,谢谢! - Karl.S
3个回答

36
创建一个使用Uint8Array对象的新缓冲区就像保存文件一样容易:
// chunk is the Uint8Array object
fs.appendFile(path, Buffer.from(chunk), function (err) {
    if (err) {
      fut.throw(err);
    } else {
      fut.return(chunk.length);
    }
});

7
new Buffer(chunk)已经被弃用,请使用Buffer.from(chunk)。 - PRAJIN PRAKASH

29

Karl.S 的回答 基础上,这个方法对我很有效,不依赖于任何框架:

fs.appendFileSync(outfile, Buffer.from(arrayBuffer));

11
新的缓冲区构造函数已经被弃用,建议使用Buffer.from(arrayBuffer)代替。 - tornord
1
不建议使用同步版本的I/O函数,因为它会阻塞事件循环,除非你有充分的理由这样做。 - Karl.S

2

我想补充一点,在更新的Meteor中,您可以使用async/await来避免一些回调地狱。使用await也会抛出错误并将其传递到客户端。

Meteor.methods({
  uploadFileData: async function(file_id, chunk) {
    var path = 'somepath/' + file_id; // be careful with this, make sure to sanitize file_id
    await fs.appendFile(path, new Buffer(chunk));
    return chunk.length;
  }
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接