0
votes

I am trying to upload a text file (also tried PDF, etc) to Salesforce. Text file contains 'hello world'.

Here is the code I am using

def putFile(sf, libname, filen):
    file_name=os.path.basename(filen)
    libId=libraryExists(sf, libname)
    contentDocumentId = getContentDocumentId(sf, libname, file_name)
    if not libId:
        print(f"Provided library '{libname}' does not exists")
        return

    with open(filen, "rb") as f:
        bodyEncoded = base64.b64encode(f.read())

    boundary = '----------------------------741e90d31eff'
    headers = {
        'Content-Type' : 'multipart/form-data; boundary=' + boundary
    }

    nonBinaryPart = '--'+boundary+'\nContent-Disposition: form-data; name="entity_content";\n'
    nonBinaryPart += 'Content-Type: application/json;\r\n\r\n'
    nonBinaryPart += json.dumps({
        "ContentDocumentId" : contentDocumentId,
        "ReasonForChange" : "Large file upload", 
        "PathOnClient" : file_name
    })
    nonBinaryPart += '\r\n\r\n'

    header = '--'+boundary+'\nContent-Disposition: form-data; name="VersionData"; filename="'+file_name+'";\nContent-Type: application/octet-stream\r\n\r\n'
    footer = '--'+boundary+'--'
    headerEncoded = header
    last4Bytes = bodyEncoded[len(bodyEncoded)-4:len(bodyEncoded)]
    print(type(last4Bytes))
    print(last4Bytes)
    if last4Bytes.endswith(b'=='):
        last4Bytes = last4Bytes[0:2] + b'0K'
        bodyEncoded = bodyEncoded[0:len(bodyEncoded)-4] + last4Bytes
        footerEncoded = footer
        reqBody = headerEncoded+str(bodyEncoded)+footerEncoded
    elif last4Bytes.endswith(b'='):
        print('Ends with =')
        last4Bytes = last4Bytes[0:3] + b'N'
        bodyEncoded = bodyEncoded[0:len(bodyEncoded)-4] + last4Bytes
        footer = '\n' + footer;
        footerEncoded = footer
        reqBody = headerEncoded+str(bodyEncoded)+footerEncoded
    else:
        footer = '\r\n' + footer
        footerEncoded = footer
        reqBody = headerEncoded+str(bodyEncoded)+footerEncoded

    reqBody = nonBinaryPart + reqBody

    print('==================================================')
    print(reqBody)
    print('==================================================')

    res = sf.contentVersion.create(reqBody, headers)

    print(res)

    print('Now downloading it...')
    os.system('rm -f ' + filen + '_downloaded')
    getFile(sf, contentDocumentId, filen + '_downloaded', './' )
    print('Downloaded.')


    os.system('md5sum ' + filen)
    os.system('md5sum ' + filen + '_downloaded')

This results in following request body which seems is according to Salesforce guidelines: https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_sobject_insert_update_blob.htm

Headers:

Content-Type: multipart/form-data; boundary="----------------------------741e90d31eff" Accept: application/json

Request body:

------------------------------741e90d31eff Content-Disposition: form-data; name="entity_content"; Content-Type: application/json;

{"ContentDocumentId": "0699E000000lKbLQAU", "ReasonForChange": "Large file upload", "PathOnClient": "hello_world.txt"}

------------------------------741e90d31eff Content-Disposition: form-data; name="VersionData"; filename="hello_world.txt"; Content-Type: application/octet-stream

b'aGVsbG8gd29ybGQK' ------------------------------741e90d31eff--

2
In the uploaded file I see not 'hello world' but the b'aGVsbG8gd29ybGQKNorayr Amirkhanyan
Hello I am also working with transfer files to salesforce. However I am working with large sized files. In my case I want read the files in a stream and dynamically append multi-part headers to the request body. I saw bodyEncoded = base64.b64encode(f.read()) helps you to read all the file content in memory, do you have any suggestion to make this also work for using file stream?user1457053

2 Answers

0
votes

Like you code shows bodyEncoded = base64.b64encode(f.read()), the file is sent as base64 encoded. You need to decode it after you download the file to restore it's original "readable" value.

Note: like your comments say, the content of your file is b'aGVsbG8gd29ybGQK', where the b indicates a base64 encoded string, and the other part is the encoded value, that you can encode also with an online tool like base64decode, that will show that the string is exactly hello world

0
votes

Finally I have got it. So to upload to Salesforce as multipart form data: 1. No base64 encoding!!!! It needs to stay as binary 2. My mistake was that I was trying to concatenate string to bytes. So, build your non binary part of multipart message and encode it to binary:

nonbinaryPart.encode()
  1. Than append bytes to bytes, binary contents of the file.
  2. When calling api, post data as bytes. Be careful as API of usage, say simple-salsforce by default may want to encode it as json. No follow up encoding is required. Post as binary.