如何使用httr(适用于Google Drive API)发布multipart/related内容

7
我使用httr成功实现了将文件上传到Google Drive的简单操作。但是每个文件都会被上传为“untitled”,因此我需要使用PATCH请求设置文件标题。然而,这种方式有时会失败。根据API文档,我应该可以使用多部分上传的方式,在同一个POST请求中指定文件标题和上传文件。
res<-POST(
  "https://www.googleapis.com/upload/drive/v2/files?convert=true",
  config(token=google_token),
  body=list(y=upload_file(file))
)
id<-fromJSON(rawToChar(res$content))$id
if(is.null(id)) stop("Upload failed")
url<-paste(
  "https://www.googleapis.com/drive/v2/files/",
  id,
  sep=""
)
title<-strsplit(basename(file), "\\.")[[1]][1]
Sys.sleep(2)
res<-PATCH(url,
  config(token=google_token),
  body=paste('{"title": "',title,'"}', sep = ""),
  add_headers("Content-Type" = "application/json; charset=UTF-8")
)
stopifnot(res$status_code==200)
cat(id)

What I'd like to do is something like this:

res<-POST(
  "https://www.googleapis.com/upload/drive/v2/files?uploadType=multipart&convert=true",
  config(token=google_token),
  body=list(y=upload_file(file),
            #add_headers("Content-Disposition" = "text/json"),
            json=toJSON(data.frame(title))
  ),
  encode="multipart",
  add_headers("Content-Type" = "multipart/related"),
  verbose()
)

我得到的输出显示个别部分的内容编码是错误的,这导致了400错误:

-> POST /upload/drive/v2/files?uploadType=multipart&convert=true HTTP/1.1
-> User-Agent: curl/7.19.7 Rcurl/1.96.0 httr/0.6.1
-> Host: www.googleapis.com
-> Accept-Encoding: gzip
-> Accept: application/json, text/xml, application/xml, */*
-> Authorization: Bearer ya29.ngGLGA9iiOrEFt0ycMkPw7CZq23e6Dgx3Syjt3SXwJaQuH4B6dkDdFXyIC6roij2se7Fs-Ue_A9lfw
-> Content-Length: 371
-> Expect: 100-continue
-> Content-Type: multipart/related; boundary=----------------------------938934c053c6
-> 
<- HTTP/1.1 100 Continue
>> ------------------------------938934c053c6
>> Content-Disposition: form-data; name="y"; filename="db_biggest_tables.csv"
>> Content-Type: application/octet-stream
>> 

>> table    rows    DATA    idx total_size  idxfrac

>> 
>> ------------------------------938934c053c6
>> Content-Disposition: form-data; name="json"
>> 
>> {"title":"db_biggest_tables"}
>> ------------------------------938934c053c6--

<- HTTP/1.1 400 Bad Request
<- Vary: Origin
<- Vary: X-Origin
<- Content-Type: application/json; charset=UTF-8
<- Content-Length: 259
<- Date: Fri, 26 Jun 2015 18:50:38 GMT
<- Server: UploadServer
<- Alternate-Protocol: 443:quic,p=1
<- 

有没有办法为单个部分正确设置内容编码?例如,第二部分应该是"text/json"。

我已经查阅了R文档、Hadley的httr项目页面、这个网站和一些普通的谷歌搜索。我找不到任何关于如何进行多部分上传和设置内容编码的示例。


嗯,我认为目前没有其他方法(除了将JSON保存到磁盘并使用upload_file())。你能否在GitHub上提交一个错误报告? - hadley
非常愉快!感谢您的快速回复。已提交错误报告 - iaina
你正在使用一个非常旧的 httr 版本。请先更新。 - Jeroen Ooms
1个回答

15

您可以使用 curl::form_file 或其别名 httr::upload_file 来完成此操作。另请参阅 curl 的 vignette。根据 Google API 文档的示例:

library(httr)

media <- tempfile()
png(media, with = 800, height = 600)
plot(cars)
dev.off()

metadata <- tempfile()
writeLines(jsonlite::toJSON(list(title = unbox("My file"))), metadata)

#post
req <- POST("https://httpbin.org/post",
  body = list(
    metadata = upload_file(metadata, type = "application/json; charset=UTF-8"),
    media = upload_file(media, type = "image/png")
  ),
  add_headers("Content-Type" = "multipart/related"),
  verbose()
)

unlink(media)
unlink(metadata)

唯一的区别在于,curl会为每个文件自动添加一个Content-Disposition头,这对于multipart/form-data是必需的,但对于multipart/related则不是。在这种情况下,服务器可能会忽略此多余的头。

目前还没有办法在不将内容写入文件的情况下完成此操作。也许我们可以在httr/curl的未来版本中添加类似的功能,尽管这以前从未出现过。


将元数据写入临时文件对我来说已经足够好了。这种策略适用于httr版本0.6.1和1.0.0.9000。 - iaina
1
请注意,上述代码需要安装“jsonlite”和“httr”包。 - iaina
除了 curl::form_file 之外,还有一个 curl::form_data。如果需要的话,实现看起来足够简单,可以复制并修改。https://github.com/jeroen/curl/blob/master/R/form.R - Roland Weber
@jeroen 如何在单个 API 调用中上传多张图片? - user5249203

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接