Range 和 Content-Range HTTP 标头是两个完全不同的东西。见Difference between Content-Range and Range headers?
Content-Range 用于指定包含Content-Range 标头的同一消息正文中的字节范围。
Range 标头用于请求来自服务器的一系列字节。响应消息将通过其自己的Content-Range 指示响应正文中发送的实际范围。
所以,这就解释了为什么在使用 TIdHTTP.Ranges 属性时会收到“缺少 Content-Range”错误。该属性根本不适合您使用它的目的。
至于使用TIdHTTP.Request.CustomHeaders属性发送Content-Range标头,这是正确的方法(技术上,TIdEntityHeaderInfo有ContentRange...属性,但它们目前只有TIdHTTP.Response使用,不是TIdHTTP.Request - that needs to be fixed)。
您的自定义 Content-Range 标头的问题在于服务器只是将其视为错误而拒绝。这很可能意味着您使用的 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 只是为了将其转换回字节进行传输,因此只需按原样发送原始文件字节,例如:
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 字段在每个成功的响应中。