Search code examples
amazon-web-servicesamazon-s3digital-oceandigital-ocean-spaces

AWS S3 POST Policy content-length-range doesn't work for exact file size match


I'd like to add file size validation to our signed urls, making sure the client uploads a file of exactly the size I signed it with. However, when I do this :

"conditions": [
    {"acl": "public-read"},
    .... ,
    ["content-length-range", 1024, 1024]
]

For small files, it works.
But a bit larger files, for example 25mb, it results in EntityTooSmall error.

It starts working only if I set the minimum to 0 like this :

["content-length-range", 0, 1024]

But I want to enforce a specific file size, and not a range.
Does S3 not support exact file size match?

EDIT : Here's the full code I compiled in Python :

# Create an S3 client
s3 = boto3.client(
    's3',
    aws_access_key_id="...",
    aws_secret_access_key="...",
    region_name="sfo3",
    endpoint_url="https://sfo3.digitaloceanspaces.com",
)

# Specify the bucket name
bucket_name = 'my_bucket'

# Specify the file name
file_name = 'SampleJPGImage_30mbmb.jpeg'

# Get the file size in bytes
file_size = os.path.getsize(file_name)

# Use the file size and content
print("File size:", file_size, "bytes") # Should be 30789588

# Specify the desired file size in bytes
file_size_min = file_size
file_size_max = file_size

# Sign the policy with your AWS secret key
signedPolicy = s3.generate_presigned_post(
    Bucket=bucket_name,
    Key=file_name,
    Fields={
        "acl": "public-read",
        "key": file_name,
    },
    Conditions=[
        {"acl": "public-read"},
        {"key": file_name},
        ["content-length-range", file_size_min, file_size_max]
    ],
    ExpiresIn=3600
)

# Use the signed policy to upload the file
with open(file_name, 'rb') as f:
    print()
    resp = requests.post(signedPolicy['url'], data=signedPolicy['fields'], files={'file': f})
    print(resp)
    print(resp.content)

Solution

  • I managed to test with Postman an exact file size and everything is working correctly. The condition that I used is:

    const Conditions = [
        ["content-length-range", 30789588, 30789588],
        ["eq", "$Content-Type", "image/jpeg"]
    ];
    

    I downloaded the 30MB file from here: https://sample-videos.com/download-sample-jpg-image.php

    When I change the size of the content length to 30789587 I get the following:

    <Error>
        <Code>EntityTooLarge</Code>
        <Message>Your proposed upload exceeds the maximum allowed size</Message>
        <ProposedSize>30789588</ProposedSize>
        <MaxSizeAllowed>30789587</MaxSizeAllowed>
    </Error> 
    

    And with a content length of 30789589:

    <Error>
        <Code>EntityTooSmall</Code>
        <Message>Your proposed upload is smaller than the minimum allowed size</Message>
        <ProposedSize>30789588</ProposedSize>
        <MinSizeAllowed>30789589</MinSizeAllowed>
    </Error> 
    

    Test with the same file I used and let's see if it works for you.

    Edit

    Executing the Python code to DigitalOcean Spaces gave the same error: EntityTooSmall without any other information.

    Executing the same code to AWS S3 is working correctly. Also, the error message, when I change the range, is more useful.

    It seems something with the range is not implemented correctly in DigitalOcean Spaces