SharePoint Online调用IBM Cloud文件上传API遇跨域及请求错误
Let's break down your problem first: you're stuck between two conflicting issues—using SP.WebProxy.invoke fixes SharePoint's CORS problem but fails because the IBM API rejects your JSON-formatted file body; using FormData directly works for IBM but gets blocked by CORS in SharePoint. The fix is to combine the best of both approaches: use SP.WebProxy to handle cross-domain requests, while constructing a proper multipart/form-data request body that IBM's API expects.
Why Your Previous Attempts Failed
- Attempt 1 Issue: IBM Visual Recognition's API requires files to be sent as
multipart/form-data, not JSON. Your code was wrapping the file content in a JSON object, which the API couldn't parse correctly. - Attempt 2 Issue: Directly sending an XMLHttpRequest to IBM's domain triggers browser CORS restrictions, since IBM's server doesn't include SharePoint's domain in its CORS allowlist.
Working Solution: Proxy + Proper Multipart Request
We'll use SP.WebProxy to forward the request, and manually construct a valid multipart/form-data body to upload your file to IBM's API. Here's the step-by-step code:
1. Helper Functions to Prepare File Data
First, we need to read the file as a raw buffer and convert it to a string that preserves binary data (using Latin1 encoding, since each byte maps directly to a character):
// Read file as ArrayBuffer and convert to Latin1 string (preserves binary data) function getFileBuffer(file, callback) { const reader = new FileReader(); reader.readAsArrayBuffer(file); reader.onload = () => { const buffer = new Uint8Array(reader.result); const bufferStr = String.fromCharCode(...buffer); callback(bufferStr); }; reader.onerror = (error) => { console.error('Error reading file:', error); }; } // Construct valid multipart/form-data body function constructMultipartBody(fileName, fileBuffer) { const boundary = `----WebKitFormBoundary${Math.random().toString(16).slice(2)}`; const parts = [ `--${boundary}`, `Content-Disposition: form-data; name="images_file"; filename="${fileName}"`, 'Content-Type: image/png', // Adjust based on your file type '', // Empty line separating headers from content fileBuffer, `--${boundary}--`, '' ]; return { boundary: boundary, body: parts.join('\r\n') }; }
2. Main Function to Call IBM API via SharePoint Proxy
This function uses SP.WebProxy to forward the request, with the properly formatted multipart body:
function uploadToIBMVisualRecognition() { const file = document.getElementById('imagem').files[0]; if (!file) { alert('Please select a file first!'); return; } getFileBuffer(file, (fileBuffer) => { const multipartData = constructMultipartBody(file.name, fileBuffer); const ibmApiUrl = 'https://gateway-a.watsonplatform.net/visual-recognition/api/v3/classify?api_key={YOUR-API-KEY}&version=2016-05-20'; $.ajax({ url: '../_api/SP.WebProxy.invoke', type: 'POST', data: JSON.stringify({ requestInfo: { '__metadata': { type: 'SP.WebRequestInfo' }, Url: ibmApiUrl, Method: 'POST', Headers: { results: [ { '__metadata': { type: 'SP.KeyValue' }, Key: 'Accept', Value: 'application/json', ValueType: 'Edm.String' }, { '__metadata': { type: 'SP.KeyValue' }, Key: 'Content-Type', Value: `multipart/form-data; boundary=${multipartData.boundary}`, ValueType: 'Edm.String' } ] }, Body: multipartData.body } }), headers: { 'Accept': 'application/json;odata=verbose', 'Content-Type': 'application/json;odata=verbose', 'X-RequestDigest': document.getElementById('__REQUESTDIGEST').value }, success: (data) => { alert('Success!'); try { const response = JSON.parse(data.d.Invoke.Body); console.log('IBM API Response:', response); } catch (e) { console.log('Raw Response:', data.d.Invoke.Body); } }, error: (xhr, status, error) => { alert('Request failed. Check console for details.'); console.error('Error Status:', status); console.error('Error Message:', error); console.error('Response:', xhr.responseText); } }); }); }
Key Notes
- API Parameter Name: IBM's Visual Recognition API expects the file parameter to be named
images_file(notimagemTesteas in your second attempt). Double-check the official docs for any updates. - Content-Type Matching: Ensure the
Content-Typeheader's boundary matches the one used in the multipart body. This is critical for the API to parse the request correctly. - File Type: Adjust the
Content-TypeinconstructMultipartBodyif you're uploading non-PNG files (e.g.,image/jpegfor JPGs). - Permissions: Make sure the current SharePoint user has permission to use
SP.WebProxy—this is enabled by default for most users in SharePoint Online.
内容的提问来源于stack exchange,提问作者David Silva

