I want my react frontend to upload, delete images from AWS S3 using presigned URLs. I created a small application to test it out. Although I do get a presigned URL, the presigned URL seems to get into a ERR_NAME_NOT_RESOLVED
tangle with the browser.
I tried to do a CURL, but CURL complains that it is a badly formatted URL the moment I put the presigned URL. CURL too gives me the same message - ERR_NAME_NOT_RESOLVED
. I am not using AWS SDK for JS v3. This is v2 - I am in test drive mode. Once I get the flow, will upgrade to v3.
Here's my code
server.js
const express = require('express');
const app = express();
const { uploadURL } = require('./awss3bucket');
const path = require('path');
app.use(express.static('src'))
app.get('/', (req, res)=>{
res.sendFile(path.join(__dirname, '/src', 'client.html'));
})
app.get('/getPresignedURL', async (req, res)=>{
const uploadUnifiedResourceLocator = await presignedURLGenerator();
res.send(uploadUnifiedResourceLocator);
})
app.listen(3000, ()=>{
console.log('app is listening');
});
awsbucket.js
const dotenv = require('dotenv');
dotenv.config();
const AWS = require('aws-sdk');
AWS.config.update({
region: process.env.REGION,
accessKeyId: process.env.ACCESS_KEY,
secretAccessKey: process.env.SECRET_KEY,
signatureVersion: 'v4'
});
const s3 = new AWS.S3();
const randomID = parseInt(Math.random() * 10000000)
const Key = `${randomID}.png`;
const s3Params = {
Bucket: process.env.BUCKET_NAME,
Key,
Expires: 3600,
ContentType: 'image/png'
}
const uploadURL = async ()=>{
let preSgnURL = await s3.getSignedUrlPromise('putObject', {
Bucket: process.env.BUCKET_NAME,
Key,
Expires: 3600,
ContentType: 'image/png'
})
return(preSgnURL);
};
module.exports = { uploadURL}
import React, { useState } from 'react';
import { createRoot } from 'react-dom/client';
import axios from 'axios';
function Client(){
const [ file, setFile ] = useState('');
const uploadURLtoS3 = ()=>{
if(file == ''){
alert('choose an image to upload');
return;
}
axios.get('/getPresignedURL')
.then(
(res)=>{
axios.put(res.data, file).then(resp => console.log(resp)).catch(err => console.log(err))
}
).catch(err => console.log(err))
}
const inputFieldChange = (e) => {
setFile(e.target.files[0]);
}
return (
<>
<h2>Upload Image</h2>
<input type="file" onChange={inputFieldChange}/>
<button onClick={uploadURLtoS3}>Submit</button>
</>
)
}
const root = createRoot(document.getElementById('root'));
root.render(<Client />);
The presigned URL that I get from the server.js code is of this form:
https://bucket-name.s3./%27ap-south-1%27;.amazonaws.com/6356661.png?Content-Type=image%2Fpng&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=%27K0000000000000000000000000000009us-east-1%27%3B%2Fs3%2Faws4_request&X-Amz-Date=20240609T110931Z&X-Amz-Expires=3600&X-Amz-Signature=0000000000000000000000000000f&X-Amz-SignedHeaders=host
Is this presigned URL badly formatted? Or is my code wrong? Or do I need to upgrade to AWS SDK for JS v4? or do I need to do something with my firewall/DNS?
Based on @JohnRotenstein's comment:
The
/%27ap-south-1%27;
part of the pre-signed URL looks wrong. The DNS name should be something likebucket-name.s3.ap-south-1.amazonaws.com
. Perhaps yourprocess.env.REGION
environment variable contains some unwanted quotes?
I found the solution was to add all variables in the .env
files without any quotes.