尝试解决使用其/upload端点将文件上传到Google云端硬盘的问题。即使尝试将简单的纯文本格式上传为文件,我仍然遇到Malformed multipart body.
错误。
以下.net c#代码用于创建请求:
string fileName = "test.txt";
string fileContent = "The quick brown fox jumps over the lazy dog";
var fileStream = GenerateStreamFromString(fileContent); // simple text string to Stream conversion
var streamContent = new StreamContent(fileStream);
streamContent.Headers.ContentType = new MediaTypeHeaderValue("text/plain");
var multiPartFormDataContent = new MultipartFormDataContent("not_so_random_boundary");
// rfc2387 headers with boundary
multiPartFormDataContent.Headers.Remove("Content-Type");
multiPartFormDataContent.Headers.TryAddWithoutValidation("Content-Type", "multipart/related; boundary=" + "not_so_random_boundary");
// metadata part
multiPartFormDataContent.Add(new StringContent("{\"name\":\"" + fileName + "\",\"mimeType\":\"text/plain\",\"parents\":[\"" + folder.id + "\"]}", Encoding.UTF8, "application/json"));
// media part (file)
multiPartFormDataContent.Add(streamContent);
var response_UploadFile = await httpClient.PostAsync(string.Format("https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart"), multiPartFormDataContent);
我记录了以下请求:
Method: POST,
RequestUri: 'https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart',
Version: 1.1,
Content: System.Net.Http.MultipartFormDataContent,
Headers: { Authorization: Bearer /*snip*/ Content-Type: multipart/related; boundary=not_so_random_boundary }
具有以下请求内容(已修饰):
--not_so_random_boundary
Content-Type: application/json; charset=utf-8
Content-Disposition: form-data
{"name":"test.txt","mimeType":"text/plain","parents":["/*snip*/"]}
--not_so_random_boundary
Content-Type: text/plain
Content-Disposition: form-data
The quick brown fox jumps over the lazy dog
--not_so_random_boundary--
我花了整整一整天的时间,这使我明白了这一点。我预感这个问题有点愚蠢,但我只是想不通。
有人可以将视线转移到这个位置上吗,也许您可以发现我犯了一个错误,这将非常有帮助?
答案 0 :(得分:1)
我认为multipart/related
的请求正文的结构可能不正确。那么如何进行如下修改?
--not_so_random_boundary
Content-Type: application/json; charset=utf-8
Content-Disposition: form-data; name="metadata"
{"name":"test.txt","mimeType":"text/plain","parents":["/*snip*/"]}
--not_so_random_boundary
Content-Type: text/plain
Content-Disposition: form-data; name="file"
The quick brown fox jumps over the lazy dog
--not_so_random_boundary--
name
的每个部分添加Content-Disposition
。https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart
的端点时,将创建具有test.txt
内容的The quick brown fox jumps over the lazy dog
文本文件。 如果这不起作用,我表示歉意。
答案 1 :(得分:1)
由于@Tanaike的建议,我们发现了我的代码存在的问题。
结果虽然没有在文档(或任何代码示例)中未特别提及,但在请求正文的Content-Disposition: form-data; name="metadata"
部分中添加StringContent
可以带来全部不同。
最终请求可以重写如下:
// sample file (controlled test example)
string fileName = "test.txt";
string fileType = "text/plain";
string fileContent = "The quick brown fox jumps over the lazy dog";
var fileStream = GenerateStreamFromString(fileContent); // test file
// media part (file)
//var fileStream = File.Open(path_to_file, FileMode.Open, FileAccess.Read); // you should read file from disk
var streamContent = new StreamContent(fileStream);
streamContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data");
streamContent.Headers.ContentDisposition.Name = "\"file\"";
// metadata part
var stringContent = new StringContent("{\"name\":\"" + fileName + "\",\"mimeType\":\"" + fileType + "\",\"parents\":[\"" + folder.id + "\"]}", Encoding.UTF8, "application/json");
stringContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data");
stringContent.Headers.ContentDisposition.Name = "\"metadata\"";
var boundary = DataTime.Now.Ticks.ToString(); // or hard code a string like in my previous code
var multiPartFormDataContent = new MultipartFormDataContent(boundary);
// rfc2387 headers with boundary
multiPartFormDataContent.Headers.Remove("Content-Type");
multiPartFormDataContent.Headers.TryAddWithoutValidation("Content-Type", "multipart/related; boundary=" + boundary);
// request body
multiPartFormDataContent.Add(stringContent); // metadata part - must be first part in request body
multiPartFormDataContent.Add(streamContent); // media part - must follow metadata part
var response_UploadFile = await httpClient.PostAsync(string.Format("https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart"), multiPartFormDataContent);
请注意,通常情况下,系统会在StreamContent
中添加文件名和内容类型,但Google云端硬盘API会忽略这些标头。这是有意完成的,因为API希望接收具有相关属性的元数据对象。 (以下标头已从上述代码示例中删除,但将保留在此处以供将来参考)
streamContent.Headers.ContentDisposition.FileName = "\"" + fileName + "\"";
streamContent.Headers.ContentType = new MediaTypeHeaderValue(fileType);
请注意,如果您要将文件上传到Google云端硬盘中的子文件夹,则只需指定"parents":["{folder_id}"]
属性。
希望这对以后的人有帮助。
答案 2 :(得分:0)
另一种选择是使用 Google .net 客户端库,让它为您处理上传。
// Upload file Metadata
var fileMetadata = new Google.Apis.Drive.v3.Data.File()
{
Name = "Test hello uploaded.txt",
Parents = new List() {"10krlloIS2i_2u_ewkdv3_1NqcpmWSL1w"}
};
string uploadedFileId;
// Create a new file on Google Drive
await using (var fsSource = new FileStream(UploadFileName, FileMode.Open, FileAccess.Read))
{
// Create a new file, with metadata and stream.
var request = service.Files.Create(fileMetadata, fsSource, "text/plain");
request.Fields = "*";
var results = await request.UploadAsync(CancellationToken.None);
if (results.Status == UploadStatus.Failed)
{
Console.WriteLine($"Error uploading file: {results.Exception.Message}");
}
// the file id of the new file we created
uploadedFileId = request.ResponseBody?.Id;
}