使用 multipart-form 将文件上传到 Salesforce

Posted

技术标签:

【中文标题】使用 multipart-form 将文件上传到 Salesforce【英文标题】:Upload a file to Salesforce using multipart-form 【发布时间】:2020-01-15 08:42:16 【问题描述】:

我正在尝试将文本文件(也尝试过 PDF 等)上传到 Salesforce。 文本文件包含“hello world”。

这是我正在使用的代码

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

这会导致以下请求正文似乎符合 Salesforce 指南: https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_sobject_insert_update_blob.htm

标题:

内容类型:multipart/form-data;边界="----------------------------741e90d31eff" 接受:application/json

请求正文:

------------------741e90d31eff 内容处置:表单数据;名称=“实体内容”; 内容类型:application/json;

"ContentDocumentId": "0699E000000lKbLQAU", "ReasonForChange": "大文件上传", "PathOnClient": "hello_world.txt"

------------------741e90d31eff 内容处置:表单数据;名称="版本数据";文件名="hello_world.txt"; 内容类型:application/octet-stream

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

【问题讨论】:

在上传的文件中,我看到的不是“hello world”,而是 b'aGVsbG8gd29ybGQK 您好,我也在处理将文件传输到 salesforce。但是我正在处理大型文件。在我的情况下,我想读取流中的文件并将多部分标头动态附加到请求正文中。我看到 bodyEncoded = base64.b64encode(f.read()) 可以帮助您读取内存中的所有文件内容,您有什么建议让这也适用于使用文件流吗? 【参考方案1】:

我终于明白了。因此,要作为多部分表单数据上传到 Salesforce: 1.没有base64编码!!! 它需要保持二进制 2. 我的错误是我试图将字符串连接到字节。 因此,构建多部分消息的非二进制部分并将其编码为二进制:

nonbinaryPart.encode()
    比将字节追加到字节,文件的二进制内容。 调用 api 时,以字节形式发送数据。使用 API 时要小心,默认情况下 simple-salsforce 可能希望将其编码为 json。不需要后续编码。以二进制形式发布。

【讨论】:

【参考方案2】:

就像您的代码显示 bodyEncoded = base64.b64encode(f.read()) 一样,文件以 base64 编码形式发送。您需要在下载文件后对其进行解码以恢复其原始“可读”值。

注意:就像你的cmets说的,你文件的内容是b'aGVsbG8gd29ybGQK',其中b表示一个base64编码的字符串,另一部分是编码值,你可以还可以使用base64decode 之类的在线工具进行编码,这将显示字符串正是hello world

【讨论】:

执行 base64 编码首先是一个错误。内容必须保持二进制,不做任何修改。

以上是关于使用 multipart-form 将文件上传到 Salesforce的主要内容,如果未能解决你的问题,请参考以下文章

PyQt5 和 Django:如何使用 HTTP 请求(Multipart-form)上传图像?

JSF/Prime(FileUpload 组件)multipart-form 不加载托管 bean 类参数

在使用 Multer 将文件上传到文件夹之前删除和创建文件夹

如何使用 AngularJS 将文件上传到 Google Drive

如何使用 NodeJS 将文件或图像上传到服务器

如何使用nestJS将文件上传到mongoose上的mongodb?