skip to Main Content

Following this Shopify tutorial, I’m trying to upload an image to Shopify. A subtask is to translate this curl command to a python request. The file is uploaded by users and I pass the file variable to this function with request.FILES[‘filename’]:

curl -v 
  -F "Content-Type=image/png" 
  -F "success_action_status=201" 
  -F "acl=private" 
  -F "key=tmp/45732462614/products/7156c27e-0331-4bd0-b758-f345afaa90d1/watches_comparison.png" 
  -F "x-goog-date=20221024T181157Z" 
  -F "x-goog-credential=merchant-assets@shopify-tiers.iam.gserviceaccount.com/20221024/auto/storage/goog4_request" 
  -F "x-goog-algorithm=GOOG4-RSA-SHA256" 
  -F "x-goog-signature=039cb87e2787029b56f498beb2deb3b9c34d96da642c1955f79225793f853760906abbd894933c5b434899d315da13956b1f67d8be54f470571d7ac1487621766a2697dfb8699c57d4e67a8b36ea993fde0f888b8d1c8bd3f33539d8583936bc13f9001ea3e6d401de6ad7ad2ae52d722073caf250340d5b0e92032d7ad9e0ec560848b55ec0f943595578a1d6cae53cd222d719acb363ba2c825e3506a52b545dec5be57074f8b1b0d58298a0b4311016752f4cdb955b89508376c38f8b2755fce2423acb3f592a6f240a21d8d2f51c5f740a61a40ca54769a736d73418253ecdf685e15cfaf7284e6e4d5a784a63d0569a9c0cffb660028f659e68a68fb80e" 
  -F "policy=eyJjb25kaXRpb25zIjpbeyJDb250ZW50LVR5cGUiOiJpbWFnZVwvcG5nIn0seyJzdWNjZXNzX2FjdGlvbl9zdGF0dXMiOiIyMDEifSx7ImFjbCI6InByaXZhdGUifSxbImNvbnRlbnQtbGVuZ3RoLXJhbmdlIiwxLDIwOTcxNTIwXSx7ImJ1Y2tldCI6InNob3BpZnktc3RhZ2VkLXVwbG9hZHMifSx7ImtleSI6InRtcFwvZ2NzXC80NTczMjQ2MjYxNFwvcHJvZHVjdHNcLzcxNTZjMjdlLTAzMzEtNGJkMC1iNzU4LWYzNDVhZmFhOTBkMVwvd2F0Y2hlc19jb21wYXJpc29uLnBuZyJ9LHsieC1nb29nLWRhdGUiOiIyMDIyMTAyNFQxODExNTdaIn0seyJ4LWdvb2ctY3JlZGVudGlhbCI6Im1lcmNoYW50LWFzc2V0c0BzaG9waWZ5LXRpZXJzLmlhbS5nc2VydmljZWFjY291bnQuY29tXC8yMDIyMTAyNFwvYXV0b1wvc3RvcmFnZVwvZ29vZzRfcmVxdWVzdCJ9LHsieC1nb29nLWFsZ29yaXRobSI6IkdPT0c0LVJTQS1TSEEyNTYifV0sImV4cGlyYXRpb24iOiIyMDIyLTEwLTI1VDE4OjExOjU3WiJ9" 
  -F "file=@/Users/shopifyemployee/Desktop/watches_comparison.png" 
   "https://shopify-staged-uploads.storage.googleapis.com/"

I use chatgpt to come up with this python code:

def uploadImage(stagedTarget, file):
    parameters = stagedTarget["parameters"]
    url = stagedTarget["url"]
    files = {
        'file': file,
    }
    data = {
        'Content-Type': parameters[0]['value'],
        'success_action_status': parameters[1]['value'],
        'acl': parameters[2]['value'],
        'key': parameters[3]['value'],
        'x-goog-date': parameters[4]['value'],
        'x-goog-credential': parameters[5]['value'],
        'x-goog-algorithm': parameters[6]['value'],
        'x-goog-signature': parameters[7]['value'],
        'policy': parameters[8]['value'],
    }
    print(f"{url = }, {data = }")
    response = requests.post(url, files=files, data=data)
    print(f"{response.status_code = }")
    print(f"{response.text = }")

    response = response.content
    response = json.loads(response)

The server gives me this response:

web_1      | response.status_code = 400
web_1      | response.text = "<?xml version='1.0' encoding='UTF-8'?><Error><Code>EntityTooSmall</Code><Message>Your proposed upload is smaller than the minimum object size specified in your Policy Document.</Message><Details>Content-length smaller than lower bound on range</Details></Error>"

My file size is only 46KB. I don’t know why it’s too small. Itried a larger file and it’s still the same. I tried to call the curl command to upload a similar local image file and it’s ok. What did I do wrong?

Updated:
I try to update the python code like below and it gives me 400 error again:

    parameters = stagedTarget["parameters"]
    url = stagedTarget["url"]
    headers = {
        'Content-Type': 'multipart/form-data'
    }
    data = {
        'Content-Type': parameters[0]['value'],
        'success_action_status': parameters[1]['value'],
        'acl': parameters[2]['value'],
        'key': parameters[3]['value'],
        'x-goog-date': parameters[4]['value'],
        'x-goog-credential': parameters[5]['value'],
        'x-goog-algorithm': parameters[6]['value'],
        'x-goog-signature': parameters[7]['value'],
        'policy': parameters[8]['value'],
        'file': file,
    }
    response = requests.post(url, headers=headers, data=data)
web_1      | response.status_code = 400
web_1      | response.text = 'Bad content type.  Please use multipart.'

2

Answers


  1. Chosen as BEST ANSWER

    Turns out the file has to be opened in binary mode. I didn't open the file

    def uploadImage(stagedTarget, file):
        # Get the permannent access_token
        parameters = stagedTarget["parameters"]
        url = stagedTarget["url"]
        files = {
            'Content-Type': parameters[0]['value'],
            'success_action_status': parameters[1]['value'],
            'acl': parameters[2]['value'],
            'key': parameters[3]['value'],
            'x-goog-date': parameters[4]['value'],
            'x-goog-credential': parameters[5]['value'],
            'x-goog-algorithm': parameters[6]['value'],
            'x-goog-signature': parameters[7]['value'],
            'policy': parameters[8]['value'],
            'file': file.open(mode='rb'),
        }
        response = requests.post(url, files=files)
        print(f"{response.status_code = }")
        print(f"{response.text = }")
    
        response = response.content
        response = json.loads(response)
    

  2. You need to pass an opened file object (likely in binary mode). Don’t pass just the filename: pass open(request.FILES['filename'], 'rb') as second argument to uploadImage().

    Although it’s a bit surprising that requests doesn’t raise an error when passed a string for the files input. But I assume a string is just file contents for requests, albeit in non-binary mode, so requests doesn’t notice the difference.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search