minio / minio

The Object Store for AI Data Infrastructure

Home Page:https://min.io/download

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Order of properties in the body of a POST upload breaks the upload

zanzlender opened this issue · comments

NOTE

I wanted to create a POST upload, because I had a need for the POST Policy which restricted what files could be uploaded, which a presignedPutUrl doesn't have.

TLDR; I do not know if this is intended behavior, but the order of the properties in the body of a POST request upload matters, and the wrong order will lead to a The body of your POST request is not well-formed multipart/form-data. (The name of the uploaded key is missing) Error.

I went through the example in the the docs, while trying to not use the superagent library, which didn't work, but when using the library everything was ok. I compared the request the superagent library made vs my own XHR and fetch requests. The only difference I noticed was the order of the properties in the formData (body) part of the request. Specifically, in my requests the file was the first property - since it had been automatically inserted when the form was submitted - while the superagent had it as the last property, as was generated with the code from the docs.

Expected Behavior

I would expect that the order of the properties wouldn't matter.

Current Behavior

If the file is before the key property it will throw an error (The name of the uploaded key is missing)

Comparing superagent (1st image) and my requests (2nd image)

image

Trying to change the order

However, when I put the file as the last property everything worked.

image

image

In order, it's the superagent library XHR request, then my Fetch request followed by my own XHR.

Possible Solution

If this is intended behaviour I don't mind, but I would think a note next to the code examples could prevent such errors.

Steps to Reproduce (for bugs)

  1. Create a MinIO instance (mine was using Docker)
  2. Have NodeJS for the backend (to generate policies - but technically it's only important to generate it anyway you wish)
  3. I used React, but Vanilla HTML and JS (haven't tested for others) should do for the Frontend
  4. Generate PostPolicy
const postPolicy = minioClient.newPostPolicy();
postPolicy.setBucket("tasky-app");
postPolicy.setKey("example.png");
postPolicy.setExpires(new Date(Date.now() + 60 * 60 * 24));
postPolicy.setContentType("text/plain");
postPolicy.setContentDisposition("attachment; filename=example.png");
// 1KB - 10MB
postPolicy.setContentLengthRange(1024, 1024 * 1024 * 100);
postPolicy.setUserMetaData({
  key: "value",
  smoke: "data",
});
const presignedPost = await minioClient.presignedPostPolicy(postPolicy);
console.log(presignedPost);
  1. Create simple form and handle upload
<form
  name="myform"
  encType="multipart/form-data"
  method="POST"
  onSubmit={async (e) => {
    e.preventDefault();

    const formData = new FormData(e.currentTarget);
    // copied here for simplicity - copy the one you created instead
    const postPolicy = {
        postURL: 'http://localhost:8001/tasky-app',
        formData: {
          bucket: 'tasky-app',
          key: 'example.png',
          'Content-Type': 'text/plain',
          'Content-Disposition': 'attachment; filename=example.png',
          'x-amz-meta-key': 'value',
          'x-amz-meta-smoke': 'data',
          'x-amz-date': '20240513T103640Z',
          'x-amz-algorithm': 'AWS4-HMAC-SHA256',
          'x-amz-credential': 'JJtetbdFP9nyGrG4x7iQ/20240513/us-east-1/s3/aws4_request',
          policy: 'eyJjb25kaXRpb25zIjpbWyJlcSIsIiRidWNrZXQiLCJ0YXNreS1hcHAiXSxbInN0YXJ0cy13aXRoIiwiJGtleSIsInByb2ZpbGUtaW1hZ2VzL3NvbWVuYW1laWRrLnBuZyJdLFsiZXEiLCIkQ29udGVudC1UeXBlIiwidGV4dC9wbGFpbiJdLFsiZXEiLCIkQ29udGVudC1EaXNwb3NpdGlvbiIsImF0dGFjaG1lbnQ7IGZpbGVuYW1lPWV4YW1wbGUucG5nIl0sWyJjb250ZW50LWxlbmd0aC1yYW5nZSIsMTAyNCwxMDQ4NTc2MDBdLFsiZXEiLCIkeC1hbXotbWV0YS1rZXkiLCJ2YWx1ZSJdLFsiZXEiLCIkeC1hbXotbWV0YS1zbW9rZSIsImRhdGEiXSxbImVxIiwiJHgtYW16LWRhdGUiLCIyMDI0MDUxM1QxMDM2NDBaIl0sWyJlcSIsIiR4LWFtei1hbGdvcml0aG0iLCJBV1M0LUhNQUMtU0hBMjU2Il0sWyJlcSIsIiR4LWFtei1jcmVkZW50aWFsIiwiSkp0ZXRiZEZQOW55R3JHNHg3aVEvMjAyNDA1MTMvdXMtZWFzdC0xL3MzL2F3czRfcmVxdWVzdCJdXSwiZXhwaXJhdGlvbiI6IjIwMjQtMDUtMTNUMTA6Mzg6MDYuNjczWiJ9',
          'x-amz-signature': '90c4bdaf683fb13e7dc7701b5a45d506e44e2fda693145a2c112fd48ca98d359'
        }
      }

    Object.keys(postPolicy.formData).forEach((key) => {
      formData.append(key, postPolicy.formData[key]);
    });

    try {
      const response = await fetch(postPolicy.postURL, {
        method: "POST",
        mode: "cors",
        body: formData,
      });

      console.log("response", response);
    } catch (err) {
      console.log("error", err);
    }
  }}
>
  <input type="file" name="file" />

  <button className="border-4 bg-red-500 text-white" type="submit">
    Submit
  </button>
</form>

Context

I was trying to upload files using POST and POST policies, from the client, because of the restrictions I can specify on the files being uploaded.

Regression

No

Your Environment

  • Version used (minio --version):
  • Server setup and configuration:
  • Operating System and version (uname -a):

minio version RELEASE.2024-05-07T06-41-25Z (commit-id=b413ff9fdbecab75e9a4ee40f5f8390f37ad752c)
Runtime: go1.22.2 linux/amd64
License: GNU AGPLv3 - https://www.gnu.org/licenses/agpl-3.0.html
Copyright: 2015-2024 MinIO, Inc.

  • MinIO instance created using the Docker image.
  • Windows 10 WSL2
  • NodeJS backend with React frontend

Community member helped solve this... #19727 (reply in thread)_

The order does matter as is written in the AWS S3 API docs...