Indy 10分段上传到OneDrive错误 [英] Indy 10 Multipart upload to OneDrive Error
问题描述
我正在尝试使用POST分段上传到OneDrive,并获取'HTTP /1.1 400错误的请求". IdLogFile:
I am trying to do Multipart upload to OneDrive using POST and get 'HTTP/1.1 400 Bad Request'. IdLogFile:
Stat Connected.
Sent 10.02.2017 12:50:08: POST /v1.0/drive/root::/children HTTP/1.0`<EOL>`Content-Type: multipart/related; boundary="Boundary"`<EOL>`Content-Length: 254`<EOL>`Authorization: Bearer EwA...%3d`<EOL>`Host: api.onedrive.com`<EOL>`Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8`<EOL>`Accept-Encoding: identity`<EOL>`User-Agent: Mozilla/3.0 (compatible; Indy Library)`<EOL><EOL>`
Sent 10.02.2017 12:50:08: --Boundary`<EOL>`Content-ID: <metadata>`<EOL>`Content-Type: application/json`<EOL>`{"name":"Dest.txt", "file":{}, "@content.sourceUrl":"cid:content"}`<EOL>`--Boundary`<EOL>`Content-ID: <content><EOL>Content-Type: application/octet-stream<SourceContent>--Boundary--
Recv 10.02.2017 12:50:08: H
Recv 10.02.2017 12:50:08: TTP/1.1 400 Bad Request`<EOL>`Via: 1.1 DM5SCH102210409 (wls-colorado)`<EOL>`Content-Length: 60`<EOL>`Content-Type: application/json`<EOL>`Server: Microsoft-IIS/8.5`<EOL>`P3P: CP="BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo"`<EOL>`X-WLSPROXY: DM5SCH102210409`<EOL>`X-MSNSERVER: DM5SCH102231823`<EOL>`Strict-Transport-Security: max-age=31536000; includeSubDomains`<EOL>`X-QosStats: {"ApiId":0,"ResultType":2,"SourcePropertyId":0,"TargetPropertyId":42}`<EOL>`X-ThrowSite: 1479.b891`<EOL>`X-AsmVersion: UNKNOWN; 16.0.0.0`<EOL>`X-MSEdge-Ref: Ref A: A9918FA26FAF469EB3797E9DAEA3172E Ref B: FRAEDGE0409 Ref C: Fri Feb 10 01:50:09 2017 PST`<EOL>`Date: Fri, 10 Feb 2017 09:50:09 GMT`<EOL>`Connection: close`<EOL><EOL>`{"error":{"code":"invalidRequest","message":"Bad Argument"}}
Stat Disconnected.
代码:
procedure TSaveFilter.UploadTest;
const
URL = 'https://api.onedrive.com/v1.0/drive/root::/children';
Boundary = 'Boundary';
var
IdHTTP: TIdHTTP;
MemoryStream: TMemoryStream;
FileStream: TFileStream;
procedure WriteLnString(str: AnsiString; CRLF: Boolean = True);
begin
if CRLF then str := str + #13#10;
MemoryStream.Write(str[1], Length(str));
end;
begin
IdHTTP := TIdHTTP.Create(nil);
try
IdHTTP.HandleRedirects := True;
IdHTTP.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(IdHTTP);
IdHTTP.Request.BasicAuthentication := False;
IdHTTP.Request.CustomHeaders.Values['Authorization'] := 'Bearer ' + FAccessToken;
IdHTTP.Request.ContentType := Format('multipart/related; boundary="%s"', [Boundary]);
MemoryStream := TMemoryStream.Create;
try
WriteLnString('--' + Boundary);
WriteLnString('Content-ID: <metadata>');
WriteLnString('Content-Type: application/json');
WriteLnString('{"name":"Dest.txt", "file":{}, "@content.sourceUrl":"cid:content"}');
WriteLnString('--' + Boundary);
WriteLnString('Content-ID: <content>');
WriteLnString('Content-Type: application/octet-stream', False);
FileStream := TFileStream.Create('Source.txt', fmOpenRead);
try
MemoryStream.CopyFrom(FileStream, FileStream.Size);
finally
FileStream.Free;
end;
WriteLnString('--' + Boundary + '--', False);
IdHTTP.Post(URL, MemoryStream);
finally
MemoryStream.Free;
end;
finally
IdHTTP.Free;
end;
end;
我在做什么错了?
这是一个工作请求(列出孩子):
This is a working request (List children):
Stat Connected.
Sent 10.02.2017 20:52:42: GET /v1.0/drive/root::/children?select=name,folder,file HTTP/1.1`<EOL>`Authorization: Bearer EwA...%3d`<EOL>`Host: api.onedrive.com`<EOL>`Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8`<EOL>`Accept-Encoding: identity`<EOL>`User-Agent: Mozilla/3.0 (compatible; Indy Library)`<EOL>``<EOL>`
Recv 10.02.2017 20:52:43: H
Recv 10.02.2017 20:52:43: TTP/1.1 200 OK`<EOL>`Via: 1.1 BN2BAP4ED8CB55D (wls-colorado)`<EOL>`Content-Length: 213`<EOL>`Content-Type: application/json; odata.metadata=minimal`<EOL>`Server: Microsoft-IIS/8.5`<EOL>`P3P: CP="BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo"`<EOL>`X-WLSPROXY: BN2BAP4ED8CB55D``X-MSNSERVER: DM5SCH102231619`<EOL>`Strict-Transport-Security: max-age=31536000; includeSubDomains`<EOL>`OData-Version: 4.0`<EOL>`X-AsmVersion: UNKNOWN; 16.0.0.0`<EOL>`X-MSEdge-Ref: Ref A: ECB06A4BE05B478AB36611C892C36CC7 Ref B: AM1EDGE0419 Ref C: Fri Feb 10 09:52:43 2017 PST``Date: Fri, 10 Feb 2017 17:52:43 GMT`<EOL>``<EOL>`{"@odata.context":"https://api.onedrive.com/v1.0/$metadata#drives('me')/items('root')/children(name,folder,file)","value":[{"name":"AB","folder":{"childCount":7}},{"name":"ArecaBackup","folder":{"childCount":6}}]}
Stat Disconnected.
推荐答案
您的MIME数据格式错误,这就是服务器拒绝它的原因.
Your MIME data is malformed, that is why the server is rejecting it.
这是您要发送的请求:
POST /v1.0/drive/root::/children HTTP/1.0
Content-Type: multipart/related; boundary="Boundary"
Content-Length: 254
Authorization: Bearer EwA...%3d
Host: api.onedrive.com
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: identity
User-Agent: Mozilla/3.0 (compatible; Indy Library)
--Boundary
Content-ID: <metadata>
Content-Type: application/json
{"name":"Dest.txt", "file":{}, "@content.sourceUrl":"cid:content"}
--Boundary
Content-ID: <content>
Content-Type: application/octet-stream<SourceContent>--Boundary--
如您所见,MIME数据都被弄乱了.具体来说,每个MIME字段都缺少一些必需的CRLF
.就像HTTP标头和正文一样,MIME标头和正文由<CRLF><CRLF>
序列分隔,并且TFileStream
数据与其后的MIME边界之间必须有一个CRLF
.
As you can see, the MIME data is all messed up. Specifically, each MIME field is missing some required CRLF
s. Just like with the HTTP headers and body, MIME headers and body are separated by a <CRLF><CRLF>
sequence, and there needs to be a CRLF
between the TFileStream
data and the MIME boundary that follows it.
请求看起来应该更像这样:
The request needs to look more like this instead:
POST /v1.0/drive/root::/children HTTP/1.0
Content-Type: multipart/related; boundary="Boundary"
Content-Length: 260
Authorization: Bearer EwA...%3d
Host: api.onedrive.com
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: identity
User-Agent: Mozilla/3.0 (compatible; Indy Library)
--Boundary
Content-ID: <metadata>
Content-Type: application/json
{"name":"Dest.txt", "file":{}, "@content.sourceUrl":"cid:content"}
--Boundary
Content-ID: <content>
Content-Type: application/octet-stream
<SourceContent>
--Boundary--
尝试使用以下代码填充TMemoryStream
:
Try this code to populate the TMemoryStream
:
MemoryStream := TMemoryStream.Create;
try
WriteLnString('--' + Boundary);
WriteLnString('Content-ID: <metadata>');
WriteLnString('Content-Type: application/json');
WriteLnString(''); // <-- ADD THIS!!!
WriteLnString('{"name":"Dest.txt", "file":{}, "@content.sourceUrl":"cid:content"}');
WriteLnString('--' + Boundary);
WriteLnString('Content-ID: <content>');
WriteLnString('Content-Type: application/octet-stream'); // <-- REMOVE THE FALSE!!!
WriteLnString(''); // <-- ADD THIS!!!
FileStream := TFileStream.Create('Source.txt', fmOpenRead);
try
MemoryStream.CopyFrom(FileStream, 0);
finally
FileStream.Free;
end;
WriteLnString(''); // <!-- ADD THIS!!!
WriteLnString('--' + Boundary + '--', False);
...
finally
MemoryStream.Free;
end;
也就是说,Indy有一个TIdMultipartFormDataStream
类,在发送multipart/form-data
帖子时通常与TIdHTTP
一起使用. OneDrive不支持multipart/form-data
,但是奇怪的是,OneDrive的文档明确指定了以下内容,该内容仅适用于multipart/form-data
而不适用于multipart/related
:
That being said, Indy has a TIdMultipartFormDataStream
class that is typically used with TIdHTTP
when sending multipart/form-data
posts. OneDrive does not support multipart/form-data
, but what is odd is that OneDrive's documentation clearly specifies the following, which only applies to multipart/form-data
and not to multipart/related
:
如果包含两个以上的部分,则该请求将被拒绝. 每个部分都必须在
Content-Disposition
标头中指定一个name
值,以指示它是哪个部分.零件可以采用任何顺序,但应首先指定元数据零件.
The request will be rejected if more than two parts are included. Each part must specify a
name
value in theContent-Disposition
header that indicates which part it is. Parts can be in either order, but should specify the metadata part first.
但是,同一文档中给出的示例正在使用multipart/related
,就像您的代码一样. Microsoft/OneDrive论坛和各种博客中都有关于在上载到OneDrive时使用multipart/form-data
还是multipart/related
的讨论.一位OneDrive员工确实确认此问题需要最终解决.
However, the example given in the same documentation is using multipart/related
, just like your code is. There are discussions in Microsoft/OneDrive forums and various blogs regarding whether to use multipart/form-data
or multipart/related
when uploading to OneDrive. One OneDrive employee did confirm this issue needs some work on their end.
仅在OneDrive支持multipart/form-data
的情况下,这是使用TIdMultipartFormDataStream
的示例:
Just in case OneDrive ever supports multipart/form-data
, here is a example using TIdMultipartFormDataStream
:
procedure TSaveFilter.UploadTest;
const
URL = 'https://api.onedrive.com/v1.0/drive/root::/children';
var
IdHTTP: TIdHTTP;
PostData: TIdMultipartFormDataStream;
begin
IdHTTP := TIdHTTP.Create(nil);
try
IdHTTP.HandleRedirects := True;
IdHTTP.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(IdHTTP);
IdHTTP.Request.BasicAuthentication := False;
IdHTTP.Request.CustomHeaders.Values['Authorization'] := 'Bearer ' + FAccessToken;
PostData := TIdMultipartFormDataStream.Create;
try
PostData.AddFormField('metadata', '{"name":"Dest.txt", "file":{}}', 'utf-8', 'application/json');
PostData.AddFile('content', 'Source.txt', 'application/octet-stream').FileName := '';
IdHTTP.Post(URL, PostData);
finally
PostData.Free;
end;
finally
IdHTTP.Free;
end;
end;
这篇关于Indy 10分段上传到OneDrive错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!