H
Hono2mo ago
Minh

Image uploaded to S3 cannot be displayed when run on Lambda

I'm trying to upload images to S3 via AWS Lambda using Hono, but when I open the image url it shows The image cannot be displayed because it contains error My Setup: - Frontend: SvelteKit, sending a multipart/form-data request with the image. - Backend: AWS Lambda with Hono Here's my route
routes.put("/:channelId", async (c: Context) => {
const channelId = c.req.param("channelId");

const formData = await c.req.formData();

const updatedChannel = await ChannelService.updateChannel(
channelId,
formData
);

return c.json(updatedChannel, 200);
});
routes.put("/:channelId", async (c: Context) => {
const channelId = c.req.param("channelId");

const formData = await c.req.formData();

const updatedChannel = await ChannelService.updateChannel(
channelId,
formData
);

return c.json(updatedChannel, 200);
});
The code for the uploading part is in the comment. It works perfectly fine on my local machine but once run on Lambda, the image is corrupted for whatever reason
20 Replies
Minh
MinhOP2mo ago
ChannelService.updateChannel
async function updateChannel(channelId: string, formData: FormData) {
let profileImgUrl: string = "";
let bannerUrl: string = "";

const profileImg = formData.get("profile_img");

if (profileImg && typeof profileImg === "object" && "name" in profileImg) {
profileImgUrl = await S3Service.uploadFile(profileImg);
}

const bannerImg = formData.get("banner");

if (bannerImg && typeof bannerImg === "object" && "name" in bannerImg) {
bannerUrl = await S3Service.uploadFile(bannerImg);
}

// logic to update the channel info in the DB
return updatedChannel;
}
async function updateChannel(channelId: string, formData: FormData) {
let profileImgUrl: string = "";
let bannerUrl: string = "";

const profileImg = formData.get("profile_img");

if (profileImg && typeof profileImg === "object" && "name" in profileImg) {
profileImgUrl = await S3Service.uploadFile(profileImg);
}

const bannerImg = formData.get("banner");

if (bannerImg && typeof bannerImg === "object" && "name" in bannerImg) {
bannerUrl = await S3Service.uploadFile(bannerImg);
}

// logic to update the channel info in the DB
return updatedChannel;
}
S3Service
const S3Service = {
uploadFile: async (file: File) => {
if (!bucketName || !region) {
throw new Error("Missing required bucket name and region");
}

const fileId = v4();

const fileExtension = file.name.split(".").pop();
const fileKey = `${fileId}.${fileExtension}`;

const arrayBuffer = await file.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);

const params = {
Body: buffer,
Bucket: bucketName,
Key: fileKey,
ContentType: file.type,
};

const uploadCommand = new PutObjectCommand(params);
const response = await s3Client.send(uploadCommand);

if (!response) return "Failed to upload file";

const url = `https://${bucketName}.s3.${region}.amazonaws.com/${fileKey}`;
return url;
},
};
const S3Service = {
uploadFile: async (file: File) => {
if (!bucketName || !region) {
throw new Error("Missing required bucket name and region");
}

const fileId = v4();

const fileExtension = file.name.split(".").pop();
const fileKey = `${fileId}.${fileExtension}`;

const arrayBuffer = await file.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);

const params = {
Body: buffer,
Bucket: bucketName,
Key: fileKey,
ContentType: file.type,
};

const uploadCommand = new PutObjectCommand(params);
const response = await s3Client.send(uploadCommand);

if (!response) return "Failed to upload file";

const url = `https://${bucketName}.s3.${region}.amazonaws.com/${fileKey}`;
return url;
},
};
ambergristle
ambergristle2mo ago
1. have you checked your S3? what's getting uploaded, or not? 2. have you confirmed that the file.type is being set correctly in deployment? you might also want to try using c.parseBody instead of c.formData parseBody actually works w multipart form data, and is the hono-recommended way to deal w file uploads: https://hono.dev/examples/file-upload while formData specifically returns a FormData instance
Minh
MinhOP2mo ago
@ambergristle 1. the image is uploaded successfully but when I view it it says The image cannot be displayed because contains error 2. yes it's correct. Here's what I see in CloudWatch
FormData {
banner: '',
profile_img: File {
size: 311948,
type: 'image/jpeg',
name: 'cat.jpg',
lastModified: 1737954587729
},
name: "My Channel",
description: 'This is My channel',
id: '...'
}
FormData {
banner: '',
profile_img: File {
size: 311948,
type: 'image/jpeg',
name: 'cat.jpg',
lastModified: 1737954587729
},
name: "My Channel",
description: 'This is My channel',
id: '...'
}
3. I tried c.parseBody but still no luck
ambergristle
ambergristle2mo ago
what's the logged content type of the files you're trying to upload?
Minh
MinhOP2mo ago
what do you mean? the type is jpeg
ambergristle
ambergristle2mo ago
ah, i see, you shared already: 'image/jpeg' huh do all the filenames have extensions?
Minh
MinhOP2mo ago
ambergristle
ambergristle2mo ago
sorry, it's been a minute since i used aws. i tried to avoid it i feel like in the past, i've needed to generate signed s3 urls. the s3 client has a method called getSignedUrl. maybe that would help?
Minh
MinhOP2mo ago
I have no idea 😅 I’ve tried Google, Gpt but nothing works. I guess I have to use something else
ambergristle
ambergristle2mo ago
oh, i meant to link this: https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#getSignedUrl-property i'd never discourage someone from ditching AWS, lol, but i hope signing helps
Minh
MinhOP2mo ago
Thanks! I will try this one turns out the problem lies in AWS API Gateway. I have to add a new multipart/form-data entry to the Binary Media Types section. AWS documentation is the worst
ambergristle
ambergristle2mo ago
that's one of the reasons i avoid AWS like the plague, lol Google products too, like firebase. docs and DX couldn't be worse if they wanted to make devs miserable
Minh
MinhOP2mo ago
seriously, how can such a big company produce such poor docs 🤦‍♂️
ambergristle
ambergristle2mo ago
i've been running my hono apps on bun, using the Koyeb platform, but i haven't used it for anything production-ready yet because they don't care. these companies don't deal treat competition as an opportunity to improve their product they're monopolies that crush or absorb independent alternatives their approach to gaining marketshare isn't better tools, it's just eliminating the alernatives but since they're massive and insanely rich, it's hard to beat the price-point, so a lot of projects end up using them anyways
Minh
MinhOP2mo ago
sadly true. It’s been mostly free using aws compared to a vps for my project so I have to use it
ambergristle
ambergristle2mo ago
ah, i see. what are you using it for?
Minh
MinhOP2mo ago
for hosting my hono app. I tried digitalocean but the pricing is too high for me at this early stage
ambergristle
ambergristle2mo ago
what does your app do? you tried koyeb?
Minh
MinhOP2mo ago
no I haven't. It looks kinda like Vercel?
ambergristle
ambergristle2mo ago
in my limited experience, digitalocean is a better comparison its totally agnostic towards your implementation the way i understand it, they run whatever you want on bare metal i started using it because it made it easy to run a hono app on bun in docker (w/o using FANG servers) so idk if they offer any off-the-shelf solutions their managed db options are somewhat limited atm, so that can be a drawback depending on your needs also not edge runtime. that's probably the biggest difference w vercel but it's also more flexible/better dx imo, and may be cheaper in some cases.

Did you find this page helpful?