javascript - 谷歌驱动器 API : Correct way to upload binary files via the Multipart API

标签 javascript file-upload character-encoding google-drive-api multipart

我正在尝试通过以下方式将二进制文件上传到 Google 云端硬盘 multipart upload API v3 .

这是文件内容的十六进制表示:

FF FE

出于某种原因,上述内容被编码为 UTF-8(我假设) 当我尝试发布它时,包含在多部分有效负载中:

--BOUNDARY
Content-Type: application/json

{"name": "F.ini"}

--BOUNDARY
Content-Type: application/octet-stream

ÿþ                <-- in the outbound request, this gets UTF-8 encoded
--BOUNDARY--

最终存储在服务器端的文件的十六进制表示:

C3 BF C3 BE

问题只出现在发送阶段: 如果我检查从文件中读取的内容的长度,我总是得到 2; 无论我使用 FileReader#readAsBinaryString 还是 FileReader#readAsArrayBuffer (分别生成长度为 2 的字符串和 byteLength 为 2 的 ArrayBuffer)。

这是我用来生成多部分负载的最少代码:

file = picker.files[0];    // 'picker' is a file picker
reader = new FileReader();
reader.onload = function (e) {
    content = e.target.result;
    boundary = "BOUNDARY";
    meta = '{"name": "' + file.name + '"}';
    console.log(content.length);    // gives 2 as expected

    payload = [
        "--" + boundary, "Content-Type: application/json", "", meta, "", "--" + boundary,
        "Content-Type: application/octet-stream", "", content, "--" + boundary + "--"
    ].join("\r\n");
    console.log(payload.length);    // say this gives n

    xhr = new XMLHttpRequest();
    xhr.open("POST", "/", false);
    xhr.setRequestHeader("Content-Type", "multipart/related; boundary=" + boundary);
    xhr.send(payload);              // this produces a request with a 'Content-Length: n+2' header
                                    // (corresponding to the length increase due to UTF-8 encoding)
};
reader.readAsBinaryString(file);

我的问题有两个:

  • 有没有办法避免这种自动 UTF-8 编码? (可能不是,因为 this answer 暗示 UTF-8 编码是 XHR 规范的一部分。)
  • 如果不是,“通知”Drive API 我的文件内容是 UTF-8 编码的正确方法是什么? 我尝试了这些方法,但没有成功:
    • 附加; charset=utf-8; charset=UTF-8 到二进制部分的 Content-Type header
    • 对父请求的 HTTP header 执行相同操作 (Content-Type: multipart/related; boundary=blablabla, charset=utf-8; 还尝试用分号替换逗号)

我需要多部分 API,因为 AFAIU "simple" API 不允许我上传到文件夹 (它仅通过 Slug HTTP header 接受文件名作为元数据, 而多部分情况下的 JSON 元数据对象也允许指定 parent 文件夹 ID)。 (只是想提这个,因为“简单”的 API 可以正确处理事情 当我直接将 File(来自选择器)或 ArrayBuffer(来自 FileReader#readAsArrayBuffer)作为 XHR 的有效负载时。)

我不想使用任何第三方库,因为

  • 我想让事情尽可能简单,并且
  • 撇开重新发明轮子和最佳实践不谈,任何由第三方库完成的事情也应该可以通过普通 JS 完成(这只是一个 fun exercise )。

为了完整起见,我尝试通过 GDrive 网络界面上传相同的文件,并且上传得很好; 然而,网络界面似乎对有效负载进行了 base64 编码,我宁愿避免这种情况 (因为它不必要地增加了有效载荷,尤其是对于更大的有效载荷,这是我的最终目标)。

最佳答案

这个修改怎么样?

修改点:

  • 使用 new FormData() 创建 multipart/form-data。
  • 使用 reader.readAsArrayBuffer(file) 而不是 reader.readAsBinaryString(file)
  • 将文件作为 blob 发送。在这种情况下,数据作为 application/octet-stream 发送。

修改后的脚本:

file = picker.files[0];    // 'picker' is a file picker
reader = new FileReader();
reader.onload = function (e) {
    var content = new Blob([file]);
    var meta = {name: file.name, mimeType: file.type};
    var accessToken = gapi.auth.getToken().access_token;
    var payload = new FormData();
    payload.append('metadata', new Blob([JSON.stringify(meta)], {type: 'application/json'}));
    payload.append('file', content);
    xhr = new XMLHttpRequest();
    xhr.open('post', 'https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart');
    xhr.setRequestHeader('Authorization', 'Bearer ' + accessToken);
    xhr.onload = function() {
      console.log(xhr.response);
    };
    xhr.send(payload);
};
reader.readAsArrayBuffer(file);

备注:

  • 在这个修改后的脚本中,我放置了端点和包含访问 token 的 header 。因此,请根据您的环境修改此设置。
  • 在本例中,我使用了 https://www.googleapis.com/auth/drive 范围。

引用:

在我的环境中,我可以确认此脚本有效。但如果这在您的环境中不起作用,我很抱歉。

关于javascript - 谷歌驱动器 API : Correct way to upload binary files via the Multipart API,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52081706/

相关文章:

ruby-on-rails - 回形针从哪里获得原始文件的名称?

c# - 比 BASE64 更紧凑的字节数组表示?

php - 无法在php中显示中文字符

javascript - 一旦图像可用就动态加载图像

javascript - 如何重构异步重复代码

php - Mozilla Firefox 不理解带空格的文件

r - 如何在 R 中还包含希腊字母的绘图标题中添加换行符

javascript - 这是 Node.js 中的正确代码吗? (变量作用域)

javascript - Flask with Jinja2 - 使用 Jinja2 语句将值从 Flask 传递到 Javascript

python - Django 文件上传输入验证和安全