【问题标题】:How to POST multipart binary & non-binary with httr (for Salesforce API)如何使用 httr 发布多部分二进制和非二进制(适用于 Salesforce API)
【发布时间】:2017-02-21 00:56:28
【问题描述】:

我在使用 httr 将文件 POST 到 Salesforce's REST API 时遇到了一些问题。我认为this SO question 可能会处理它,但似乎没有。我有一种预感,这是因为 Saleforce 想要一个非二进制部分,而 upload_file 创建了一个类 form_file 的对象,该对象作为二进制处理,但也许有人有其他解释/解决方案......

Salesforce 请求以下 curl 请求以插入新文档:

curl https://yourInstance.salesforce.com/services/data/v23.0/sobjects/Document/ -H
"Authorization: Bearer token" -H "Content-Type: multipart/form-data;
boundary=\"boundary_string\"" --data-binary @newdocument.json

newdocument.json 看起来像这样:

--boundary_string
Content-Disposition: form-data; name="entity_document";
Content-Type: application/json
{
"Description" : "Marketing brochure for Q1 2011",
"Keywords" : "marketing,sales,update",
"FolderId" : "005D0000001GiU7",
"Name" : "Marketing Brochure Q1",
"Type" : "pdf"
}
--boundary_string
Content-Type: application/pdf
Content-Disposition: form-data; name="Body"; filename="2011Q1MktgBrochure.pdf"
Binary data goes here.
--boundary_string--

如果我尝试使用@Jeroen 的回答作为有用的指南生成类似的输出,我会收到错误请求错误:

library(httr)
library(jsonlite)

url <- "https://[Salesforce Instance]/services/data/v39.0/sobjects/Document/"
header <- add_headers(c(Authorization = "Bearer [Salesforce Key]"))

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

metadata <- tempfile()
x <- data.frame(FolderId="a0a1300000ZG7u3", Name="test.png") #Salesforce Record ID and file name
json <- toJSON(unbox(x), pretty=T)
writeLines(json, metadata)

body <- list(entity_document = upload_file(metadata, type = "application/json; charset=UTF-8"), 
             Body = upload_file(media, type = "image/png"))


req <- POST(url, header, verbose(), body = body)

(详细输出)

-> POST /services/data/v39.0/sobjects/Document/ HTTP/1.1
-> Host: [Salesforce Instance]
-> User-Agent: libcurl/7.51.0 r-curl/2.3 httr/1.2.1.9000
-> Accept-Encoding: gzip, deflate
-> Accept: application/json, text/xml, application/xml, */*
-> Authorization: Bearer [Salesforce Key]
-> Content-Length: 3720
-> Expect: 100-continue
-> Content-Type: multipart/form-data; boundary=------------------------6525413b2350e313
-> 
<- HTTP/1.1 100 Continue
>> --------------------------6525413b2350e313
>> Content-Disposition: form-data; name="entity_document"; filename="file1510059b5200f"
>> Content-Type: application/json
>> 

>> {
>>     "FolderId": "a0a1300000ZG7u3",
>>     "Name": "test.png"
>>   }

>> 
>> --------------------------6525413b2350e313
>> Content-Disposition: form-data; name="Body"; filename="file151001ac96950"
>> Content-Type: image/png
>> 

>> ‰PNG
>> 

>> 
>> --------------------------6525413b2350e313--

<- HTTP/1.1 400 Bad Request

除了第一部分的文件名外,这与所需的输出非常接近,但 Salesforce 会返回消息:

content(req)

[[1]]
[[1]]$message
[1] "Cannot include more than one binary part"

[[1]]$errorCode
[1] "INVALID_MULTIPART_REQUEST"

我只尝试了 json,但得到了一个稍微不同的错误请求:

body <- list(entity_document= json, 
             Body = upload_file(media, type = "image/png"))

-> Content-Type: multipart/form-data; boundary=------------------------ecbd3787f083e4b1
-> 
<- HTTP/1.1 100 Continue
>> --------------------------ecbd3787f083e4b1
>> Content-Disposition: form-data; name="entity_document"
>> 
>> {
>>     "FolderId": "a0a1300000ZG7u3",
>>     "Name": "test.png"
>>   }
>> --------------------------ecbd3787f083e4b1
>> Content-Disposition: form-data; name="Body"; filename="file151001ac96950"
>> Content-Type: image/png
>> 

>> ‰PNG
>> 

>> 
>> --------------------------ecbd3787f083e4b1--

content(req)

[[1]]
[[1]]$message
[1] "Multipart message must include a non-binary part"

[[1]]$errorCode
[1] "INVALID_MULTIPART_REQUEST"

深入研究细节,在我看来,如果我使用 upload_filecurl 要么读取元数据文件并将其作为二进制文件发布,要么如果我将其发布为,则发布不带内容类型的 json特点。这是唯一的问题吗?如果是这样,有没有办法修改处理程序以接受内容类型?

非常感谢任何帮助!

【问题讨论】:

    标签: r curl salesforce multipart httr


    【解决方案1】:

    这是基于@Jeroen 对curl 的最新添加的(临时)解决方案(请参阅Github issue 以供参考):

    library(curl) #>=2.4 on Cran
    library(httr)
    library(jsonlite)
    
    url <- "https://[Salesforce Instance]/services/data/v39.0/sobjects/Document/"
    header <- add_headers(c(Authorization = "Bearer [Salesforce Key]"))
    
    path <- tempfile()
    png(path, width = 800, height = 600)
    plot(cars)
    dev.off()
    
    media <- upload_file(path)
    
    x  <- data.frame(Name=basename(path),ParentId="[Salesforce Parent ID]",Description="",IsPrivate=FALSE)
    x <- as.character(toJSON(unbox(x),pretty=T))
    metadata <- form_data(x,"application/json") #new curl function to handle a character body with defined type
    
    body <- list(metadata=metadata,Body=media)
    
    req <- httr:::request(fields=body)
    req <- httr:::request_build("POST",url,req,header)
    
    response <- httr:::request_perform(req,new_handle())
    

    我为 httr 添加了一个 request 来整合这种新的处理方式,并且我想一旦他们开始使用它,它会更简单易用,即简单的 POST(url,header,body=body)

    希望对你有帮助!

    【讨论】:

      【解决方案2】:

      这是 OP 尝试做的一个完整且有效的示例。

      library(httr)
      
      # JSON formatted metadata
      x <- list(a = 1, b = 2)
      
      # PNG media
      media <- tempfile()
      png(media, width = 800, height = 600)
      plot(cars)
      dev.off()
      
      # Construct multipart body
      #   Note the use of form_data() and form_file() which allow 
      #    the user to specify the content type of each part
      body <- list(
        metadata = curl::form_data(toJSON(x), type = "application/json"),
        media = curl::form_file(media, type = mime::guess_type(media))
      )
      
      POST(url, body, encode = "multipart")
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-10-13
        • 2021-01-01
        • 1970-01-01
        • 2021-10-11
        • 1970-01-01
        • 2016-03-01
        • 1970-01-01
        • 2015-09-13
        相关资源
        最近更新 更多