Как опубликовать составной/связанный контент с помощью httr (для API Google Диска)

У меня есть простая загрузка файлов на Google Диск, работающая с использованием httr. Проблема в том, что каждый документ загружается как «без названия», и мне нужно ИСПРАВИТЬ метаданные, чтобы установить заголовок. Запрос 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)

Я хотел бы сделать что-то вроде этого:

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 на Github, этот сайт и немного погуглил. Я не могу найти примеры того, как выполнить многокомпонентную загрузку и установить кодировку содержимого.


person iaina    schedule 26.06.2015    source источник
comment
Хммм, я не думаю, что в настоящее время его нет (за исключением сохранения json на диск и использования upload_file()). Не могли бы вы сообщить об ошибке на github?   -  person hadley    schedule 27.06.2015
comment
С удовольствием! Спасибо за быстрый ответ. Обнаружена ошибка.   -  person iaina    schedule 27.06.2015
comment
Вы используете очень старую версию httr. Сначала обновите.   -  person Jeroen    schedule 22.08.2015


Ответы (1)


Вы должны иметь возможность сделать это, используя curl::form_file или его псевдоним httr::upload_file. См. также винье curl. Следуя примеру из документа 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, хотя раньше такого не было.

person Jeroen    schedule 22.08.2015
comment
Мне достаточно записи метаданных во временный файл. Эта стратегия работает с версиями httr 0.6.1 и 1.0.0.9000. - person iaina; 24.08.2015
comment
Обратите внимание, что для приведенного выше кода требуется пакет jsonlite, а также httr. - person iaina; 24.08.2015
comment
Также есть curl::form_data рядом с curl::form_file. А реализации выглядят достаточно просто, чтобы копировать и модифицировать при необходимости. github.com/jeroen/curl/blob/master/R/form. Р - person Roland Weber; 14.02.2019
comment
@jeroen, как загрузить несколько изображений за один вызов API? - person user5249203; 25.03.2021