Indy TIdHttp put 命令针对 MS Graph 的内容范围存在问题 API
Problem with content range with Indy TIdHttp put command against MS Graph API
我正在尝试将 TIdHttp.Put()
用于 Microsoft 的 Graph API,但无法使用 Content-Range
header。如果我使用 Ranges
属性 然后我得到一个关于缺少 Content-Range
的错误,如果我在 CustomHeaders
属性 中使用这个 header然后我收到关于无效 Content-Range
.
的错误
代码如下:
sUploadSession := jsnUSession.Get('uploadUrl').JsonValue.Value;
Form1.htp2.Request.ContentType := 'application/octet-stream';
Form1.htp2.Request.ContentLength := iSize; // 3820753
Form1.htp2.Request.CustomHeaders.Clear;
//Form1.htp.Request.CustomHeaders.Add('Content-Length: ' + IntToStr(iSize));
Form1.htp2.Request.CustomHeaders.Add('Content-Range: bytes 0-' + IntToStr(iSize - 1) + '/' + IntToStr(iSize)); // 'Content-Range: bytes 0-3820750/3820751'
{with Form1.htp2.Request.Ranges.Add do
begin
StartPos := 0;
EndPos := iSize;
s := Text;
end; }
fs := TStringStream.Create('{' + TEncoding.Default.GetString(TFile.ReadAllBytes(sFile)) + '}');
bLog := True;
try
Form1.htp2.Put(sUploadSession, fs);
except
on E: EIdHTTPProtocolException do
Form1.RichEdit1.Lines.Add(E.Message + #13#10 + E.ErrorMessage);
end;
当我使用 Ranges
属性 时的消息是:
HTTP/1.1 400 Bad Request
{"error":{"code":"MissingContentRangeHeader","message":"Content-Range header is required."}}
CustomHeaders
中Content-Range
的留言是:
HTTP/1.1 400 Bad Request
{"error":{"code":"InvalidContentRangeHeader","message":"Invalid Content-Range header."}}
Indy 中的 PUT
命令是否与 HTTP 中的标准兼容,或者是否需要进行调整才能使其正常工作?
Range
和 Content-Range
HTTP header 是两个完全不同的东西。参见 Difference between Content-Range and Range headers?
Content-Range
用于指定包含 Content-Range
header.
的同一消息的 body 中的字节范围
Range
header用于请求来自服务器的字节范围。响应消息将通过它自己的 Content-Range
指示在响应中发送的实际范围 body.
所以,这就解释了为什么在使用 TIdHTTP.Ranges
属性 时出现“缺少 Content-Range
”错误。 属性 根本不适合您使用它的目的。
至于使用 TIdHTTP.Request.CustomHeaders
属性 发送 Content-Range
header,这是正确的方法(从技术上讲,TIdEntityHeaderInfo
有 ContentRange...
属性,但它们目前仅被 TIdHTTP.Response
使用,而不被 TIdHTTP.Request
- that needs to be fixed).
使用
您的自定义 Content-Range
header 的问题在于服务器只是将其视为错误而拒绝。这很可能意味着您使用的 iSize
值实际上与您实际发送的字节数不匹配。
尝试更像这样的东西:
sUploadSession := jsnUSession.Get('uploadUrl').JsonValue.Value;
fs := TStringStream.Create('{' + TEncoding.Default.GetString(TFile.ReadAllBytes(sFile)) + '}');
try
bLog := True;
try
iSize := fs.Size; // <-- the TStringStream constructor internally converted the String to TBytes...
Form1.htp2.Request.ContentType := 'application/octet-stream';
Form1.htp2.Request.ContentLength := iSize;
Form1.htp2.Request.CustomHeaders.Clear;
Form1.htp2.Request.CustomHeaders.Add('Content-Range: bytes 0-' + IntToStr(iSize - 1) + '/' + IntToStr(iSize));
Form1.htp2.Put(sUploadSession, fs);
except
on E: EIdHTTPProtocolException do
Form1.RichEdit1.Lines.Add(E.Message + #13#10 + E.ErrorMessage);
end;
finally
fs.Free;
end;
但是,没有充分的理由将文件读入解码的 UTF-16 string
只是为了将其转换回字节以进行传输,因此只需发送原始文件字节 as-is ,例如:
sUploadSession := jsnUSession.Get('uploadUrl').JsonValue.Value;
fs := TFileStream.Create(sFile, fmOpenRead or fmShareDenyWrite);
try
bLog := True;
try
iSize := fs.Size;
Form1.htp2.Request.ContentType := 'application/octet-stream';
Form1.htp2.Request.ContentLength := iSize;
Form1.htp2.Request.CustomHeaders.Clear;
Form1.htp2.Request.CustomHeaders.Add('Content-Range: bytes 0-' + IntToStr(iSize - 1) + '/' + IntToStr(iSize));
Form1.htp2.Put(sUploadSession, fs);
except
on E: EIdHTTPProtocolException do
Form1.RichEdit1.Lines.Add(E.Message + #13#10 + E.ErrorMessage);
end;
finally
fs.Free;
end;
或者:
sUploadSession := jsnUSession.Get('uploadUrl').JsonValue.Value;
fs := TBytesStream.Create(TFile.ReadAllBytes(sFile));
try
bLog := True;
try
iSize := fs.Size;
Form1.htp2.Request.ContentType := 'application/octet-stream';
Form1.htp2.Request.ContentLength := iSize;
Form1.htp2.Request.CustomHeaders.Clear;
Form1.htp2.Request.CustomHeaders.Add('Content-Range: bytes 0-' + IntToStr(iSize - 1) + '/' + IntToStr(iSize));
Form1.htp2.Put(sUploadSession, fs);
except
on E: EIdHTTPProtocolException do
Form1.RichEdit1.Lines.Add(E.Message + #13#10 + E.ErrorMessage);
end;
finally
fs.Free;
end;
或者:
sUploadSession := jsnUSession.Get('uploadUrl').JsonValue.Value;
fs := TMemoryStream.Create;
try
bLog := True;
try
fs.LoadFromFile(sFile);
fs.Position := 0;
iSize := fs.Size;
Form1.htp2.Request.ContentType := 'application/octet-stream';
Form1.htp2.Request.ContentLength := iSize;
Form1.htp2.Request.CustomHeaders.Clear;
Form1.htp2.Request.CustomHeaders.Add('Content-Range: bytes 0-' + IntToStr(iSize - 1) + '/' + IntToStr(iSize));
Form1.htp2.Put(sUploadSession, fs);
except
on E: EIdHTTPProtocolException do
Form1.RichEdit1.Lines.Add(E.Message + #13#10 + E.ErrorMessage);
end;
finally
fs.Free;
end;
无论哪种方式,请注意 Microsoft 建议在使用此 PUT
API 时一次上传的文件不要超过 4MB。你的示例文件是 3.6MB,所以它只能放在一个 PUT
请求中,但要知道对于更大的文件,你将不得不将它们分成多个 4MB 的上传,注意 NextExpectedRanges
每个成功响应中的字段。
我正在尝试将 TIdHttp.Put()
用于 Microsoft 的 Graph API,但无法使用 Content-Range
header。如果我使用 Ranges
属性 然后我得到一个关于缺少 Content-Range
的错误,如果我在 CustomHeaders
属性 中使用这个 header然后我收到关于无效 Content-Range
.
代码如下:
sUploadSession := jsnUSession.Get('uploadUrl').JsonValue.Value;
Form1.htp2.Request.ContentType := 'application/octet-stream';
Form1.htp2.Request.ContentLength := iSize; // 3820753
Form1.htp2.Request.CustomHeaders.Clear;
//Form1.htp.Request.CustomHeaders.Add('Content-Length: ' + IntToStr(iSize));
Form1.htp2.Request.CustomHeaders.Add('Content-Range: bytes 0-' + IntToStr(iSize - 1) + '/' + IntToStr(iSize)); // 'Content-Range: bytes 0-3820750/3820751'
{with Form1.htp2.Request.Ranges.Add do
begin
StartPos := 0;
EndPos := iSize;
s := Text;
end; }
fs := TStringStream.Create('{' + TEncoding.Default.GetString(TFile.ReadAllBytes(sFile)) + '}');
bLog := True;
try
Form1.htp2.Put(sUploadSession, fs);
except
on E: EIdHTTPProtocolException do
Form1.RichEdit1.Lines.Add(E.Message + #13#10 + E.ErrorMessage);
end;
当我使用 Ranges
属性 时的消息是:
HTTP/1.1 400 Bad Request
{"error":{"code":"MissingContentRangeHeader","message":"Content-Range header is required."}}
CustomHeaders
中Content-Range
的留言是:
HTTP/1.1 400 Bad Request
{"error":{"code":"InvalidContentRangeHeader","message":"Invalid Content-Range header."}}
Indy 中的 PUT
命令是否与 HTTP 中的标准兼容,或者是否需要进行调整才能使其正常工作?
Range
和 Content-Range
HTTP header 是两个完全不同的东西。参见 Difference between Content-Range and Range headers?
Content-Range
用于指定包含 Content-Range
header.
Range
header用于请求来自服务器的字节范围。响应消息将通过它自己的 Content-Range
指示在响应中发送的实际范围 body.
所以,这就解释了为什么在使用 TIdHTTP.Ranges
属性 时出现“缺少 Content-Range
”错误。 属性 根本不适合您使用它的目的。
至于使用 TIdHTTP.Request.CustomHeaders
属性 发送 Content-Range
header,这是正确的方法(从技术上讲,TIdEntityHeaderInfo
有 ContentRange...
属性,但它们目前仅被 TIdHTTP.Response
使用,而不被 TIdHTTP.Request
- that needs to be fixed).
您的自定义 Content-Range
header 的问题在于服务器只是将其视为错误而拒绝。这很可能意味着您使用的 iSize
值实际上与您实际发送的字节数不匹配。
尝试更像这样的东西:
sUploadSession := jsnUSession.Get('uploadUrl').JsonValue.Value;
fs := TStringStream.Create('{' + TEncoding.Default.GetString(TFile.ReadAllBytes(sFile)) + '}');
try
bLog := True;
try
iSize := fs.Size; // <-- the TStringStream constructor internally converted the String to TBytes...
Form1.htp2.Request.ContentType := 'application/octet-stream';
Form1.htp2.Request.ContentLength := iSize;
Form1.htp2.Request.CustomHeaders.Clear;
Form1.htp2.Request.CustomHeaders.Add('Content-Range: bytes 0-' + IntToStr(iSize - 1) + '/' + IntToStr(iSize));
Form1.htp2.Put(sUploadSession, fs);
except
on E: EIdHTTPProtocolException do
Form1.RichEdit1.Lines.Add(E.Message + #13#10 + E.ErrorMessage);
end;
finally
fs.Free;
end;
但是,没有充分的理由将文件读入解码的 UTF-16 string
只是为了将其转换回字节以进行传输,因此只需发送原始文件字节 as-is ,例如:
sUploadSession := jsnUSession.Get('uploadUrl').JsonValue.Value;
fs := TFileStream.Create(sFile, fmOpenRead or fmShareDenyWrite);
try
bLog := True;
try
iSize := fs.Size;
Form1.htp2.Request.ContentType := 'application/octet-stream';
Form1.htp2.Request.ContentLength := iSize;
Form1.htp2.Request.CustomHeaders.Clear;
Form1.htp2.Request.CustomHeaders.Add('Content-Range: bytes 0-' + IntToStr(iSize - 1) + '/' + IntToStr(iSize));
Form1.htp2.Put(sUploadSession, fs);
except
on E: EIdHTTPProtocolException do
Form1.RichEdit1.Lines.Add(E.Message + #13#10 + E.ErrorMessage);
end;
finally
fs.Free;
end;
或者:
sUploadSession := jsnUSession.Get('uploadUrl').JsonValue.Value;
fs := TBytesStream.Create(TFile.ReadAllBytes(sFile));
try
bLog := True;
try
iSize := fs.Size;
Form1.htp2.Request.ContentType := 'application/octet-stream';
Form1.htp2.Request.ContentLength := iSize;
Form1.htp2.Request.CustomHeaders.Clear;
Form1.htp2.Request.CustomHeaders.Add('Content-Range: bytes 0-' + IntToStr(iSize - 1) + '/' + IntToStr(iSize));
Form1.htp2.Put(sUploadSession, fs);
except
on E: EIdHTTPProtocolException do
Form1.RichEdit1.Lines.Add(E.Message + #13#10 + E.ErrorMessage);
end;
finally
fs.Free;
end;
或者:
sUploadSession := jsnUSession.Get('uploadUrl').JsonValue.Value;
fs := TMemoryStream.Create;
try
bLog := True;
try
fs.LoadFromFile(sFile);
fs.Position := 0;
iSize := fs.Size;
Form1.htp2.Request.ContentType := 'application/octet-stream';
Form1.htp2.Request.ContentLength := iSize;
Form1.htp2.Request.CustomHeaders.Clear;
Form1.htp2.Request.CustomHeaders.Add('Content-Range: bytes 0-' + IntToStr(iSize - 1) + '/' + IntToStr(iSize));
Form1.htp2.Put(sUploadSession, fs);
except
on E: EIdHTTPProtocolException do
Form1.RichEdit1.Lines.Add(E.Message + #13#10 + E.ErrorMessage);
end;
finally
fs.Free;
end;
无论哪种方式,请注意 Microsoft 建议在使用此 PUT
API 时一次上传的文件不要超过 4MB。你的示例文件是 3.6MB,所以它只能放在一个 PUT
请求中,但要知道对于更大的文件,你将不得不将它们分成多个 4MB 的上传,注意 NextExpectedRanges
每个成功响应中的字段。