Skip to content

Incorrect File Content Saved When Using multipart/form-data on PUT Route #1

@macmeharder

Description

@macmeharder

🐞 Problem (Bug)
In the direct upload route (PUT /:fileKey), if the client sends the file using the multipart/form-data encoding (which can happen with certain libraries or proxies), the server incorrectly saves the entire multipart/form-data body—including form headers and boundary delimiters (--WebKitFormBoundary...)—instead of saving only the clean file bytes.

💥 Impact
Files downloaded later via the public access route (GET /f/:fileKey) are corrupted, displaying as a text dump of the multipart/form-data structure instead of the intended file content (e.g., a PNG image fails to display).

The effective file size in the storage backend is inflated due to the inclusion of unnecessary form metadata.

🔍 Root Cause
The original implementation relied solely on const blob = await c.req.blob();. When a request arrived with Content-Type: multipart/form-data, this method read the entire request body (the form packet) as a single binary Blob. This included all the form's structural text, leading to the corruption when saved.

✅ Solution
Implement explicit content type checking. If the Content-Type is multipart/form-data, the code must use the specialized form parser (c.req.formData()) to extract the clean File object (which is a Blob) before passing it to the storage adapter.

Code Fix (Diff):

// Original, problematic code:
// const blob = await c.req.blob(); 
  
// New, corrected block:
  let blob: Blob;
  const contentType = c.req.header("content-type") || "";

  if (contentType.includes("multipart/form-data")) {
    const formData = await c.req.formData();
    const file = formData.get("file");
    if (!file || !(file instanceof File)) {
      return c.json({ error: "No file provided" }, 400);
    }
    // Extract the clean File/Blob object from the FormData
    blob = file; 
  } else {
    // Handle the ideal case: pure binary PUT (clean bytes)
    blob = await c.req.blob(); 
  }
  
  const { fileHash } = await storageAdapter.upload(fileKey, blob);

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions