2014-11-22 52 views
3

我正通過JavaScript通過其REST API與Salesforce進行通信。他們期望的多/ form-data的內容類型的請求主體的格式非常具體:如何在不使用Blob的情況下將內容類型添加到多部分請求中

--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-- 

截至https://www.salesforce.com/us/developer/docs/api_rest/Content/dome_sobject_insert_update_blob.htm

看到通過下面的代碼,我已經得到了請求主體非常接近他們預期的格式,只是它不包含第一個邊界中的Content-Type(application/json)。

// Prepare binary data 
var byteString = window.atob(objectData.Body); 
var ab = new ArrayBuffer(byteString.length); 
var ia = new Uint8Array(ab); 

for (var i = 0; i < byteString.length; i++) { 
    ia[i] = byteString.charCodeAt(i); 
} 

var bb = new Blob([ab], { "type": "image/png" }); 

// Setup form 
var formData = new FormData(); 
formData.append("entity_attachment", JSON.stringify({ data: '' })); 
formData.append("Body", bb); 

var request = new XMLHttpRequest(); 
request.open("POST", myurl); 
request.send(formData); 

此代碼不會爲第一個邊界中的序列化JSON放置內容類型。這是必要的;事實上,通過Fiddler,我能夠確認當Content-Type文本存在於第一個邊界部分時,請求處理得很好。

我試圖做以下,它把串行化JSON作爲bindary數據,與給予提供所述內容類型的能力的好處:

formData.append("entity_attachment", new Blob([JSON.stringify({ data: '' })], { "type": "application/json"})); 

這產生以下HTTP:

Content-Disposition: form-data; name="entity_attachment"; filename="blob" 
Content-Type: application/json 
... 

不幸的是,Salesforce的服務器提供了以下錯誤信息:

Cannot include more than one binary part 

我可以考慮將數據格式化爲特定的Salesforce正文格式的最後一種方法是手動構建請求正文;我試圖做到這一點,但我試圖連接二進制文件時感覺不夠,我不確定如何在字符串中進行連接。

我正在尋找任何建議,以使我的請求正文與Salesforce匹配。

回答

4

This answer顯示如何手動構建包含二進制文件的multipart/form請求體,並繞過瀏覽器完成的標準UTF-8編碼。這是通過將整個請求作爲ArrayBuffer傳遞來完成的。

下面是我使用來解決問題(如圖this answer)代碼:

// { 
// Body: base64EncodedData, 
// ContentType: '', 
// Additional attachment info (i.e. ParentId, Name, Description, etc.) 
// } 

function uploadAttachment (objectData, onSuccess, onError) { 
    // Define a boundary 
    var boundary = 'boundary_string_' + Date.now().toString(); 
    var attachmentContentType = !app.isNullOrUndefined(objectData.ContentType) ? objectData.ContentType : 'application/octet-stream'; 

// Serialize the object, excluding the body, which will be placed in the second partition of the multipart/form-data request 
var serializedObject = JSON.stringify(objectData, function (key, value) { return key !== 'Body' ? value : undefined; }); 

var requestBodyBeginning = '--' + boundary 
    + '\r\n' 
    + 'Content-Disposition: form-data; name="entity_attachment";' 
    + '\r\n' 
    + 'Content-Type: application/json' 
    + '\r\n\r\n' 
    + serializedObject 
    + '\r\n\r\n' + 
    '--' + boundary 
    + '\r\n' 
    + 'Content-Type: ' + attachmentContentType 
    + '\r\n' 
    + 'Content-Disposition: form-data; name="Body"; filename="filler"' 
    + '\r\n\r\n'; 

var requestBodyEnd = 
    '\r\n\r\n' 
    + '--' + boundary + '--'; 

// The atob function will decode a base64-encoded string into a new string with a character for each byte of the binary data. 
var byteCharacters = window.atob(objectData.Body); 

// Each character's code point (charCode) will be the value of the byte. 
// We can create an array of byte values by applying .charCodeAt for each character in the string. 
var byteNumbers = new Array(byteCharacters.length); 

for (var i = 0; i < byteCharacters.length; i++) { 
    byteNumbers[i] = byteCharacters.charCodeAt(i); 
} 

// Convert into a real typed byte array. (Represents an array of 8-bit unsigned integers) 
var byteArray = new Uint8Array(byteNumbers); 

var totalRequestSize = requestBodyBeginning.length + byteArray.byteLength + requestBodyEnd.length; 

var uint8array = new Uint8Array(totalRequestSize); 
var i; 

// Append the beginning of the request 
for (i = 0; i < requestBodyBeginning.length; i++) { 
    uint8array[i] = requestBodyBeginning.charCodeAt(i) & 0xff; 
} 

// Append the binary attachment 
for (var j = 0; j < byteArray.byteLength; i++, j++) { 
    uint8array[i] = byteArray[j]; 
} 

// Append the end of the request 
for (var j = 0; j < requestBodyEnd.length; i++, j++) { 
    uint8array[i] = requestBodyEnd.charCodeAt(j) & 0xff; 
} 

return $j.ajax({ 
    type: "POST", 
    url: salesforceUrl, 
    contentType: 'multipart/form-data' + "; boundary=\"" + boundary + "\"", 
    cache: false, 
    processData: false, 
    data: uint8array.buffer, 
    success: onSuccess, 
    error: onError, 
    beforeSend: function (xhr) { 
     // Setup OAuth headers... 
    } 
}); 

};

+1

感謝您的問答!在$'.ajax'中將'cache'設置爲'false'是無用的,因爲這是一個'POST'請求。您可以刪除該參數。 – Vinay 2015-06-23 18:36:24

+1

謝謝。這個答案是挽救生命。值得注意的是,爲了使API能夠接受我的請求,我必須從邊界中移除兩個「\」。 – 2016-10-20 16:05:22

+0

對象數據的類型是什麼?在頂部的註釋部分是什麼? – wuliwong 2016-12-02 19:06:18

相關問題