Google 驱动器 API:通过 Multipart API 上传二进制文件的正确方法
Google Drive API: Correct way to upload binary files via the Multipart API
我正在尝试通过以下方式将二进制文件上传到 Google 驱动器
multipart upload API v3.
这是文件内容的十六进制表示:
FF FE
出于某种原因,上述内容被编码为 UTF-8(我假设)
当我尝试 POST 它时,包含在多部分有效负载中:
--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 的字符串和 ArrayBuffer
以及 byteLength
2)。
这是我用来生成多部分有效载荷的最少代码:
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 规范的一部分。)
- 如果不是,"inform" 我的文件内容是 UTF-8 编码的驱动器 API 的正确方法是什么?
我尝试了这些方法,但没有成功:
- 将
; charset=utf-8
或 ; charset=UTF-8
附加到二进制部分的 Content-Type
header
- 对 parent 请求上的 HTTP header 做同样的事情
(
Content-Type: multipart/related; boundary=blablabla, charset=utf-8
;
还尝试用分号替换逗号)
我需要多部分 API 因为 AFAIU "simple" API
不允许我上传到文件夹
(它只接受文件名作为元数据,通过 Slug
HTTP header,
而多部分情况下的 JSON 元数据 object 也允许指定 parent
文件夹 ID)。
(只是想提这个,因为 "simple" API 可以正确处理事情
当我直接 POST File
(来自选择器)或 ArrayBuffer
(来自 FileReader#readAsArrayBuffer
)作为 XHR 的有效载荷时。)
我不想使用任何 third-party 库,因为
- 我想让事情尽可能简单,并且
- 抛开 reinventing-the-wheel 和 best-practices 的东西,任何由第三方库完成的事情也应该可以通过纯 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);
注:
- 在这个修改后的脚本中,我放置了端点和 header,包括访问令牌。所以请根据您的环境修改它。
- 在这种情况下,我使用了
https://www.googleapis.com/auth/drive
的范围。
参考:
在我的环境中,我可以确认此脚本有效。但如果这在您的环境中不起作用,我很抱歉。
我正在尝试通过以下方式将二进制文件上传到 Google 驱动器 multipart upload API v3.
这是文件内容的十六进制表示:
FF FE
出于某种原因,上述内容被编码为 UTF-8(我假设) 当我尝试 POST 它时,包含在多部分有效负载中:
--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 的字符串和 ArrayBuffer
以及 byteLength
2)。
这是我用来生成多部分有效载荷的最少代码:
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 规范的一部分。)
- 如果不是,"inform" 我的文件内容是 UTF-8 编码的驱动器 API 的正确方法是什么?
我尝试了这些方法,但没有成功:
- 将
; charset=utf-8
或; charset=UTF-8
附加到二进制部分的Content-Type
header - 对 parent 请求上的 HTTP header 做同样的事情
(
Content-Type: multipart/related; boundary=blablabla, charset=utf-8
; 还尝试用分号替换逗号)
- 将
我需要多部分 API 因为 AFAIU "simple" API
不允许我上传到文件夹
(它只接受文件名作为元数据,通过 Slug
HTTP header,
而多部分情况下的 JSON 元数据 object 也允许指定 parent
文件夹 ID)。
(只是想提这个,因为 "simple" API 可以正确处理事情
当我直接 POST File
(来自选择器)或 ArrayBuffer
(来自 FileReader#readAsArrayBuffer
)作为 XHR 的有效载荷时。)
我不想使用任何 third-party 库,因为
- 我想让事情尽可能简单,并且
- 抛开 reinventing-the-wheel 和 best-practices 的东西,任何由第三方库完成的事情也应该可以通过纯 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);
注:
- 在这个修改后的脚本中,我放置了端点和 header,包括访问令牌。所以请根据您的环境修改它。
- 在这种情况下,我使用了
https://www.googleapis.com/auth/drive
的范围。
参考:
在我的环境中,我可以确认此脚本有效。但如果这在您的环境中不起作用,我很抱歉。