#How are you uploading them
1 messages · Page 1 of 1 (latest)
Thanks a bunch
so looks like the code from that link you posted is depreciated (At least VSCode is warning me it's depreciated) so I'm going to try this:
let decodedImage = Buffer.from(req.body, 'base64');
Look about right?
Yes
ok let me try that
const response = await fetch(url, {
method: "PUT",
body: decodedImage,
headers: {
"Content-Type": req.headers['content-type'],
"Content-Disposition": req.headers['content-disposition'],
}
})```
fraid it's still getting corrupted
does the frontend -> backend API need to do anything different?
frontend code for ref: js let file = event.target.files[0] console.log("file: ",file) let res = await fetch("/api/uploadImage", { method:"PUT", body:file, headers:{ "Content-Type":file.type, "Content-Disposition": `attachment; filename="${file.name}"`, "name":file.name, } }) let data = await res.json() < for ref
"image/jpeg"
Try making it binary/octet-stream
backend code is now:
let newFileName = crypto.randomUUID()+"."+req.headers['content-type'].split("/")[1];
let decodedImage = Buffer.from(req.body, 'base64');
const response = await fetch(url, {
method: "PUT",
body: decodedImage,
headers: {
"Content-Type": "binary/octet-stream",
"Content-Disposition": req.headers['content-disposition'],
}
})```
Still no
Whats the value of decodedImage?
this is the image I'm uploading (Just a dummy image, I know it's silly, sorry). I'm going to send the file I'm getting back out of it
Corrupted image I get out of S3
ohai
that's too small
$R �=� �&����e�,��^��k]�]�vŵ�f��
� ��i��k�inq��I{�f�a��a�Z���av�inq��Hj(�W��ʹנ����j��ݻ-��{
��IGUߵ��dP���&�����~P=���:�X�6H8�,```
Okay. So once you upload the picture. Are you able to go into the S3 bucket and view the pic?
no, it is corrupted in S3
Hmmm okay.
before I was trying to bufferize it I was getting the correct size image at least
even if the file is still corrupt
maybe "invalid" is what I should be saying instead of corrupt lol
There must be something weird about my AWS sdk because
const s3 = new AWS.S3({
I don't think S3 is a member of AWS
Let me boot up a quick app real quick to test this
thanks. I'm following this tutorial here:
https://docs.sst.dev/start/nextjs
the big exception being that there's that API call instead of directly uploading from the frontend
also I don't know what my problem was with this function signature before, it looks like it's getting through the type checker at least this time so 👍
Thats good!
ikr haha but it's still not uploading. This is my present backend code:
const response = await s3.upload({
Bucket: Bucket.PortfoliizeImageBucket.bucketName,
Key: crypto.randomUUID(),
Body: decodedImage,
ACL: "public-read"
}).promise();
console.log("response: ",response)```
Whats your response?
Fraid still not getting useful files. They're still the 153 byte thing
Are we sure that's the correct way to get the decodedImage? I suspect that might be wrong as it's consistently producing tiny files
Im checking to see.
maybe it should be converted to a buffer/B64 before upload to the backend
Yes. I thought thats what you were doing? lol
Oh no
the frontend code is this:
console.log("file: ",file)
let res = await fetch("/api/uploadImage", {
method:"PUT",
body:file,
headers:{
"Content-Type":file.type,
"Content-Disposition": `attachment; filename="${file.name}"`,
"name":file.name,
}
})
let data = await res.json()```
Honest question though. Why not juse https://uploadthing.com/ ?
Frankly? Because that's what all the tutorials are written for
The other thing is since this is SST, it lives on the cloud
Ahh.
I mean, I've been following the SST NextJS tutorial
Im deploying the bootstrap stack now. I cloned the repo from the tutorial.
ty
Where I'm at rn:
Frontend code:
const toBase64 = (file:any) => new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
});
let fileBase64 = await toBase64(file)
let res = await fetch("/api/uploadImage", {
method:"PUT",
body:fileBase64 as string,
headers:{
"name":file.name,
"type":file.type
}
})
let data = await res.json()```
https://github.com/sst/sst/tree/master/examples/quickstart-nextjs this is the repo for that Tutorial. I cloned and didnt change anything and its working just fine.
Yes, it most likely is, but I didn't do it that exact way
I want the upload code to live on the backend of my server instead of on the frontend
Okay. So what does your /api/uploadImage endpoint look like?
right now:
backend:
const response = await s3.upload({
Bucket: Bucket.PortfoliizeImageBucket.bucketName,
Key: crypto.randomUUID(),
Body: req.body,
ACL: "public-read",
ContentEncoding: 'base64',
ContentType: req.headers['type'],
}).promise();```
There's likely some kind of confusion between the encoding type and so on so bear with me.
that's just one of the links I've seen go by, that's the one I've happened to settle on, they don't seem to do much differently
{
"name": "portfoliize",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "sst bind next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.395.0",
"@aws-sdk/s3-request-presigner": "^3.395.0",
"@prisma/client": "^5.1.1",
"@types/node": "20.5.0",
"@types/react": "18.2.20",
"@types/react-dom": "18.2.7",
"eslint": "8.47.0",
"eslint-config-next": "13.4.17",
"next": "13.4.17",
"react": "18.2.0",
"react-dom": "18.2.0",
"typescript": "5.1.6"
},
"devDependencies": {
"aws-cdk-lib": "2.91.0",
"constructs": "10.2.69",
"prisma": "^5.1.1",
"sst": "^2.24.7"
}
}
Try setting stuff up with the extra layer between the file upload and the upload to S3 and see if you can get that to work
and if you can, how the hecc lol
Do you have your code on Github?
Okay. So I made an api Endpoint called uploadImage.ts
This is what my page looks like
import {Inter} from 'next/font/google';
import styles from '@/styles/Home.module.css';
const inter = Inter({subsets: ['latin']});
export default function Home() {
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
const file = (e.target as HTMLFormElement).file.files?.[0]!;
// First, get the signed URL:
const response = await fetch('/api/uploadImage', {method: 'POST'});
const data = await response.json();
const {url} = data;
const image = await fetch(url, {
body: file,
method: 'PUT',
headers: {
'Content-Type': file.type,
'Content-Disposition': `attachment; filename="${file.name}"`,
},
});
window.location.href = image.url.split('?')[0];
}
return (
<main className={styles.main}>
<form className={styles.form} onSubmit={handleSubmit}>
<input name="file" type="file" accept="image/png, image/jpeg" />
<button type="submit" className={inter.className}>
Upload
</button>
</form>
</main>
);
}
My API
import crypto from 'crypto';
import {Bucket} from 'sst/node/bucket';
import {getSignedUrl} from '@aws-sdk/s3-request-presigner';
import {S3Client, PutObjectCommand} from '@aws-sdk/client-s3';
import {NextApiRequest, NextApiResponse} from 'next';
interface Data {
url?: string;
error?: string;
}
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<Data>
) {
if (req.method !== 'POST') {
return res.status(405).end();
}
try {
const command = new PutObjectCommand({
ACL: 'public-read',
Key: crypto.randomUUID(),
Bucket: Bucket.public.bucketName,
});
const url = await getSignedUrl(new S3Client({}), command);
return res.status(200).json({url});
} catch (error) {
return res.status(500).json({error: 'Failed to get signed URL'});
}
}
And this is working?
Yes
Sweet. Let me see if I can do the old copy paste
Wait
the frontend is still doing the uploading here
I'm trying to avoid having the upload code living on the frontend at all
the file gets sent to the API and the API takes care of it
How will it get sent to the API?
That part is fine
it's fine if it goes through my frontend to my backend
but I don't want AWS code living on the frontend is all
if we're sending upload urls to the frontend that makes me uncomfortable
fair enough... Yeah I'm beginning to think through the attack vectors I'm picturing and maybe I'm exaggerating the threat
AWS is pretty secure by default. https://aws.amazon.com/s3/security/
if using the frontend for image uploads really is best practices, hell, think I should probably stop worring lol
Well how else would you upload images? Lol.
It's just weird. First you need to call the backend to get the signed URL. Then the frontend needs to call S3 to get the final submission URL. then the frontend needs to call yet another API in order to create a fileupload ORM or whatever.
As opposed to frontend just sending the file to the backend, backend uploading to S3 and doing all the backend stuff, including ORM and all that
I admit that the frontend stuff is simpler and easier to explain in a quickstart tutorial, but it feels like it can't be best practices
but if it is, I mean, sure.
Im sure there is a way to do it all on the Backend. Just not sure off the top of my head.
https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/
That may hold some answers as well.
https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-iam-policies.html
Can also lock your bucket down securetly
Alright. Got any more questions? 🙂
Thank you, I should be good