Notes:
import { S3Client, ... } from "@aws-sdk/client-s3"
const s3 = new S3Client({
credentials: {
accessKeyId: accessKey, //accessKey is a dotEnv variable
secretAccessKey: secretAccessKey, //secretAccesskey is a dotEnv variable
},
region: bucketRegion
});
Multer is middleware which handles file uploading. When using the single() function from the multer package, multer adds a file object to the req object which contains information about the uploaded file.
const storage = multer.memoryStorage()
const upload = multer({ storage: storage })
...
//routes, other code
router.post("/", upload.single('image'), async (req: any, res: any) => {
...
}
We will need to modify our event routes to account for the addition of images into the S3 bucket
For example:
In our post function:
const multer = require('multer')
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
import dotenv from "dotenv";
dotenv.config();
// connect to aws s3
const bucketName = process.env.BUCKET_NAME;
const region = process.env.BUCKET_REGION;
const accessKeyId = process.env.ACCESS_KEY!;
const secretAccessKey = process.env.SECRET_ACCESS_KEY!;
const s3Client = new S3Client({
region,
endpoint: `https://s3.${region}.amazonaws.com`,
credentials: {
accessKeyId,
secretAccessKey,
},
});
router.post("/upload", upload.single("file"), async (req: any, res: any) => {
const { file } = req;
// return error if file not found
if (file === null) {
return res.status(400).send("No file uploaded.");
}
//random fileName
const fileName =
Math.random().toString(36).substring(2, 15) +
Math.random().toString(36).substring(2, 15);
const params = {
Bucket: bucketName,
Body: file.buffer,
Key: fileName,
ContentType: file.mimetype,
};
try {
// put object into s3 bucket
await s3Client.send(new PutObjectCommand(params));
// returns key of the image
const url = `https://bucketName.s3.region.amazonaws.com/${fileName}`;
// return url
res.send(url);
} catch (error) {
res.send(error);
}
});
In the frontend, one possible solution is to send only the image as a formData() so that the image is encoded as a “multipart/formdata” file and continue to send the other parameters as a json file. In the backend, we can add just the image data onto the S3 bucket and store the image link in the MongoDB
const [file, setFile] = useState<File>();
...
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
if (!file) {
alert("Please select a file.");
return;
}
// create FormData object
const formData = new FormData();
formData.append("image", file);
try {
// upload image to S3 bucket
const response = await fetch(`${PORT}/upload"` {
method: "POST",
body: formData,
});
if (response.ok) {
const data = await response.text();
const imageURL = data; // get image url from S3 response
await fetch(`${PORT}/posts/` {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
...
}),
});
alert("Successfully uploaded");
} else {
console.error("Failed to upload image");
}
} catch (error) {
console.error(error);
}
};