What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
To upload a file from an Express app to Amazon S3, first parse the incoming multipart/form-data request, then pass the file data to an AWS SDK for JavaScript v3 S3 command. For small, explicitly bounded files, Multer’s memory storage and PutObjectCommand provide a straightforward pattern. For larger files or higher-volume workloads, avoid buffering entire files in your Node.js process and consider disk or stream-based handling, managed multipart uploads, or a presigned URL that lets the browser send the bytes directly to S3.
How the upload flow works
Express does not turn a multipart file upload into an S3 object by itself. A typical server-routed upload has four stages:
As an Amazon Associate I earn from qualifying purchases.
- The client sends a
multipart/form-datarequest to an Express route. - Middleware such as Multer parses the request and exposes the file to the route handler.
- The handler sends the file body to S3 using the AWS SDK for JavaScript v3.
- The application records the resulting object key and returns an appropriate response. Storage and public access are separate decisions; an uploaded object is not automatically safe to expose publicly.
The Express/Multer and S3 pieces below are an integration pattern, not a single AWS-prescribed end-to-end example. Configure AWS authentication before running the SDK code, and keep credentials on the server rather than in browser code. AWS recommends using the Active LTS release of Node.js for development in its Node.js SDK getting-started guide.
Choose how the file reaches S3
| Approach | Best fit | Trade-off |
|---|---|---|
Multer memory storage with PutObjectCommand |
Small files with strict size and count limits | The full file is held in a Node.js Buffer, increasing process memory use. |
Disk or stream-based handling with managed multipart Upload |
Large or stream-based uploads | Requires handling temporary storage or stream lifecycle, but avoids treating a whole in-memory Buffer as the default design. |
| Presigned browser-to-S3 upload | When file bytes should not pass through the Express server | The server must authorize URL issuance and deal safely with the uploaded object; a same-key upload replaces the existing object. |
AWS documents @aws-sdk/client-s3 for S3 operations and recommends considering multipart upload at 100 MB. That figure is guidance, not a hard limit on PutObjectCommand. AWS’s S3 console supports uploads up to 160 GB; that console maximum is not an SDK limit, and AWS directs larger uploads to the CLI, SDKs, or REST API. See AWS’s presigned URL guide, multipart upload guidance, and object upload documentation for the relevant details.
#1 Best Overall
Install and configure the SDK
Install the S3 client package in your project:
npm install @aws-sdk/client-s3
The v3 SDK separates service clients into packages. The S3 client uses send() with command objects, such as PutObjectCommand. Configure credentials using an AWS-supported authentication method before starting the application; do not hard-code secret keys in source code or send them to clients. Consult AWS’s Node.js SDK setup guide for authentication setup.
Accept a bounded multipart upload with Express and Multer
Multer is Express middleware for multipart/form-data. Its memory storage puts the file bytes in req.file.buffer. This makes a simple route possible, but it is appropriate only when file sizes and concurrent upload volume are bounded.
Rank #2
npm install express multer @aws-sdk/client-s3
Example server-side integration:
import express from "express";
import multer from "multer";
import { randomUUID } from "node:crypto";
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
const app = express();
const s3 = new S3Client({ region: process.env.AWS_REGION });
const bucket = process.env.S3_BUCKET;
const upload = multer({
storage: multer.memoryStorage(),
limits: {
fileSize: 5 * 1024 * 1024, // Example cap: 5 MiB per file
files: 1,
fields: 5,
parts: 6,
},
fileFilter: (req, file, callback) => {
// Example allowlist only; choose types that your application actually accepts.
const allowed = new Set(["image/jpeg", "image/png"]);
callback(null, allowed.has(file.mimetype));
},
});
app.post("/uploads", upload.single("file"), async (req, res, next) => {
try {
if (!req.file) {
return res.status(400).json({ error: "A supported file is required." });
}
// Use a server-generated key; do not trust a client filename as an S3 path.
const key = `uploads/${randomUUID()}`;
await s3.send(new PutObjectCommand({
Bucket: bucket,
Key: key,
Body: req.file.buffer,
ContentType: req.file.mimetype,
}));
// Return the key, not an assumed public URL. Apply your own access policy.
return res.status(201).json({ key });
} catch (error) {
return next(error);
}
});
app.use((error, req, res, next) => {
if (error instanceof multer.MulterError) {
return res.status(400).json({ error: "Upload rejected: file or request limits exceeded." });
}
console.error(error);
return res.status(500).json({ error: "Upload failed." });
});
The example uses a 5 MiB per-file cap as an application choice, not an AWS requirement. It also limits file, field, and part counts, rejects MIME types outside its example allowlist, generates an opaque key, and waits for the S3 request to complete before returning success. A submitted MIME type is user-controlled and is not, by itself, proof that the file’s contents are safe; validate files according to the risks and requirements of your application.
Set limits and choose storage deliberately
Multer documents that several upload-count and file-size limits default to infinity, so set values that reflect the workload you intend to accept. Its documentation warns: “Uploading very large files, or relatively small files in large numbers very quickly, can cause your application to run out of memory when memory storage is used.”
- Memory storage: convenient for small, bounded uploads because the route receives a Buffer. Total memory pressure depends on concurrent requests as well as the cap per file.
- Disk storage: Multer provides a file path rather than a Buffer. Account for temporary disk capacity, cleanup after success or failure, and access controls on the temporary directory.
- Streaming or managed multipart: more suitable when you should not buffer each complete file in memory. AWS’s
@aws-sdk/lib-storagepackage provides the v3Uploadhelper for managed multipart behavior. AWS demonstrates awaitingupload.done()with a Node.js file stream in its S3 checksum guide.
For managed multipart behavior, install the additional package:
npm install @aws-sdk/lib-storage
AWS identifies @aws-sdk/lib-storage as the v3 option for the multipart behavior associated with the v2 upload() method. Match the upload method to the file size, source type, and recovery needs rather than treating one command as suitable for every workload.
When to let the browser upload directly
A presigned URL is a time-limited grant to perform a specified S3 operation. A server can issue a presigned PUT for a server-chosen object key so a client can upload without receiving AWS credentials. The signed request is constrained by the signing principal’s permissions. If a presigned upload uses a key that already exists, it replaces that object. AWS explains these rules in its presigned URL documentation.
Free tools Windows power users keep installed
One-click scans. No signup required.
- Your application authenticates the user and decides whether that user may upload.
- The server selects an object key and creates a short-lived presigned PUT URL permitted by its own AWS identity.
- The client sends the file bytes to S3 using the URL, rather than sending them through the Express process.
- The application verifies the resulting object and its metadata according to its authorization and validation model before treating the upload as complete.
Presigned uploads can reduce the bandwidth and buffering work handled by Express, but they do not remove the need to authorize the upload or validate what was stored. Choose keys that avoid accidental replacement, and do not treat possession of a URL as proof that an uploaded file is safe.
Quick Recap
Best Value
Common implementation mistakes
- Using memory storage without conservative limits: large files or bursts of concurrent uploads can exhaust application memory.
- Trusting the original filename as the object key: filenames are client-controlled and can collide; generate keys server-side and retain the original name separately only if the product needs it.
- Returning success before S3 finishes: await the SDK operation and handle failures before responding with success.
- Confusing console and API limits: the 160 GB figure AWS gives for the S3 console is not a maximum imposed on SDK uploads.
- Assuming stored means public: decide separately how authorized users access objects; do not expose objects merely because an upload succeeded.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




