Upload file to s3 using presigned post url in the server - javascript

TDLR: Using s3 presigned post url to upload file to s3. Works fine on the browser but fails on the server.
I have a simple lambda function that generates presigned post url that can be consumed either in the browser or in the server.
During testing I noticed that the upload works fine one the browser but fails if I try to upload a file from a server even tho the code is identical.
The error i get is:
You must provide the Content-Length HTTP header
Detailed error:
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>MissingContentLength</Code>
<Message>You must provide the Content-Length HTTP header.</Message>
<RequestId>JP75YMFARK0G3X5Z</RequestId>
<HostId>toHsKmxmVYYAtac94cQoy8wXoregKG3PNBm97c3gQewEmKxLggcumTAP882T/pJNWx/lxRgH98A=</HostId>
</Error>
Request failed with status code 411
I checked online and found many threads about this issue but unfortunately not a single suggestion helped me.
Code I am running in the server
const axios = require('axios');
const { createReadStream, readFileSync } = require('fs');
const FormData = require('form-data');
const getPostPresignedUrl = async () => {
var config = {
method: 'post',
url: LAMBDA_GET_URL,
headers: {
'Content-Type': 'application/json',
},
data: JSON.stringify({
key: 'test-2.jpg',
fileType: 'image/jpeg',
}),
};
const {
data: { data },
} = await axios(config);
return data;
};
const uploadFileToS3 = async (fields, url) => {
const formData = new FormData();
Object.entries(fields).map(([key, value]) => {
formData.append(key, value);
});
const file = createReadStream('./test-1.jpg');
formData.append('file', file);
try {
const { data } = await axios({
url,
method: 'post',
headers: {
'Content-Type': 'multipart/form-data',
},
data: formData,
});
} catch (error) {
if (error instanceof axios.AxiosError) {
console.log(error.response.data);
}
console.log(error.message);
}
};
const init = async () => {
const { fields, url } = await getPostPresignedUrl();
await uploadFileToS3(fields, url);
};
init();
Code I am running in the browser:
const form = document.getElementById('form');
const input = document.getElementById('file');
const getPostPresignedUrl = async (name) => {
var config = {
method: 'post',
url: LAMBDA_GET_URL,
headers: {
'Content-Type': 'application/json',
},
data: JSON.stringify({
key: name,
fileType: 'image/jpeg',
}),
};
const {
data: { data },
} = await axios(config);
return data;
};
const uploadFileToS3 = async (fields, url, file) => {
const formData = new FormData();
Object.entries(fields).map(([key, value]) => {
formData.append(key, value);
});
formData.append('file', file);
try {
const { data } = await axios({
url,
method: 'post',
headers: {
'Content-Type': 'multipart/form-data',
},
data: formData,
});
} catch (error) {
if (error instanceof axios.AxiosError) {
console.log(error.response.data);
}
console.log(error.message);
}
};
const handleSubmit = async (e) => {
e.preventDefault();
const file = input.files[0];
const data = await getPostPresignedUrl(file.name);
await uploadFileToS3(data.fields, data.url, file);
};
form.onsubmit = handleSubmit;

Related

including image in request body as binary data

I need to include the image as binary data in my uploading request using multipart form data, and it seems not working, any advise will be appreciated.
my code:
const [selectedFile, setSelectedFile] = useState(null);
const handleSubmit = async (event) => {
event.preventDefault();
const formData = new FormData();
formData.append('selectedFile', new Blob([selectedFile], { type: 'application/octet-stream' }));
const data = {
uploadLink,
formData,
};
const headers = {
'Content-Type': 'application/octet-stream' ,
Accept: 'application/vnd.vimeo.*+json;version=3.4',
};
try {
await axios
.post(`${backendPostPath}/thumbnail-upload`, data, {
headers,
})
.then((response) => {
applyThumbnial();
console.log(response);
});
} catch (error) {
console.log(error);
}
};
const handleFileSelect = (event) => {
setSelectedFile(event.target.files[0]);
};
include formData as axios data parameter instead of your data object, so you can also include uploadLink in the formData:
const formData = new FormData();
formData.append('selectedFile', new Blob([selectedFile], { type: 'application/octet-stream' }));
formData.append('uploadLink', uploadLink)
//...
await axios
.post(`${backendPostPath}/thumbnail-upload`, formData, {
headers,
})

Upload files VueJS to NodeJS backend by axios

I would like to upload a file in vuejs to my nodejs server. However, I can't receive my file in the backend. Someone could help me. I have tried several things but I still can't get it. If anyone can help me I would be very grateful :-)
VueJS Upload Page Code
<input id="input-file" name="input-file" type="file" ref="file" #change="newFile($event)"/>
const formData = new FormData();
formData.append("file", document.getElementById("input-file"))
const createUserResponse = await RequestManager.executePostRequest("/users/upload", formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
});
RequestManager executePostRequest Function
static async executePostRequest(url: string, params: any, specialConfig?: AxiosRequestConfig) {
const postToken = Utils.buildHmacSha256Signature(params);
let headers: AxiosRequestHeaders = {
"x-access-token": config.server.backendAccessToken,
}
if (RequestManager.token) {
headers = {
"x-access-token": config.server.backendAccessToken,
"x-token-data": RequestManager.token,
}
}
if (specialConfig && specialConfig.headers) {
headers = Utils.mergeObjects(headers, specialConfig.headers) as AxiosRequestHeaders;
}
const instance = axios.create({
baseURL: config.server.host,
headers: headers
});
console.log(headers);
const paramsPost = {
data: params,
token: postToken
}
return new Promise<any>((resolve, reject) => {
instance
.post(url, paramsPost)
.then(response => {
resolve(response.data);
})
.catch(error => {
reject(error);
});
});
}
Backend NodeJS
UserRouter.get("/upload", (req, res) => {
console.log(req.files);
res.send("ok");
});

How to cancel file upload in Vue

I have a post method
uploadFile: async function () {
const formData = new FormData();
formData.append("file", this.file);
let url = `http://...`;
try {
this.source = axios.CancelToken.source();
const res = await axios.post(url, formData, {
headers: {
Authorization: "Token " + this.$store.getters.getToken,
"Content-Type": "multipart/form-data",
},
cancelToken: this.source.token,
});
} catch (error) {
}
}
and cancel method
cancelLoad: function () {
this.source.cancel('cancel upload');
},
The request was canceled, but after reloading my page, the file uploaded.
I've fixed it.
const CancelToken = axios.CancelToken;
let cancel;
const res = await axios.post(url, formData, {
headers: {...},
cancelToken: new CancelToken(function executor(c) {
cancel = c;
}),
}...
cancel();

How to recive uploded file in nodejs

i have a redux-from submission, console log look like this
{
id: "x",
parentId: "b",
editing: true,
logo: FileList,
}
Logo FileList contains my uploaded file name and file, i think
0: File(19355) {name: "11964821.jpeg", lastModified: ......
using fetch to send to nodejs, image contain my files and above data
function Update(image) {
const requestOptions = {
method: 'POST',
headers: { ...authHeader(), 'Content-Type': 'application/x-www-form-urlencoded' },
body: JSON.stringify(image)
};
return fetch(`${apiUrl}/API`, requestOptions).then(handleResponse)
.then( data => { return data; });
}
but this data not receiving nodejs, because is tried re.file etc.. its shows undefined
app.post('/image', upload.single('logo'), function (req, res, next) {
})
include formdata
const onSubmit = (image, dispatch)=>{
var formData = new FormData();
formData.append('logo',image.logo[0]);
var imageNew = {...image,formData};
dispatch(imageActions.companyProfileLogoUpdate(imageNew)) ;
}
now log is
formData: FormData {}
id: "5c66478b0814720a4365fe72"
logo: FileList {0: File(19355), length: 1}
parentId: "5c66478b0814720a4365fe71"
let formData = new FormData();
formData.append('logo' , {{your image file}}
// upload file as
let result = await sendServerRequestForForm(url,formData);
function sendServerRequestForForm(url,data) {
var promise = new Promise(function(resolve) {
resolve($.ajax({
url,
method : 'POST',
data,
processData: false,
contentType: false,
success: function(data) {
return data
},
error: function (err) {
try{
let responseStatus = err.responseJSON
// watch your response
}catch (e) {
console.log('Field to get response');
}
}
}));
});
return promise
}
for fetch API you can try this
let formData = new FormData();
formData.append('logo', fileList[0]);
fetch(url, {
method: 'post',
body: data,
})
.then(…);
move formData to fetch file and remove header content type, Try this
function Update(image) {
var formData = new FormData();
formData.append('logo',image.logo[0]);
const requestOptions = {
method: 'POST',
headers: { ...authHeader() },
body: formData
};
return fetch(`${apiUrl}/API`, requestOptions).then(handleResponse)
.then( data => { return data; });
}
First thing is for using fetch api for post content-type header should match typeof body. since your body is json stringified your contentType header should be 'application/json' . more about this .
Second. since you want to upload an image which a file you should use FormData without requirement of setting contentType: like below:
let formData = new FormData();
formData.append('logo', image.logo[0]);
const requestOptions = {
method: 'POST',
body: formData
};
fetch(`${apiUrl}/API`, requestOptions)
.then(handleResponse)...

Is it possible to send data and files in the same request?

I have an API that receives uploads of APP files and images
To send from APP to the API I use fetch
const data = new FormData();
let i = 0;
export const dataPush = (fileUri, fileType, fileName) => {
data.append('file'+i, {
uri: fileUri,
type: fileType,
name: fileName
});
i++;
};
export const uploadFiles = () => {
console.log(data);
fetch('http://192.168.0.23/apiapp/public/api/annex', {
method: 'post',
body: data
}).then(res => {
console.log(res)
});
}
But I'd like to send in the same request data obtained from a form
But I did not find a way to do it, always or just send the data, or just send the files
Is it possible to send everything in the same request? And if possible, how?
You just append whatever data that you desire that isn't file data to the FormData object.
data.append("not_a_file", "This is a string");
I did so based on Quentin's response and it worked
const formData = new FormData();
const i = 0;
export const filePush = (fileUri, fileType, fileName) => {
formData.append('file'+i, {
uri: fileUri,
type: fileType,
name: fileName
});
i++;
};
export const dataPush = (name, content) => {
formData.append(name, content);
};
export const uploadFiles = () => {
fetch('http://192.168.0.23/apiapp/public/api/annex', {
method: 'post',
body: formData
}).then(res => {
console.log(res._bodyText)
}).catch(error => {
console.log(error.message)
});
}

Categories

Resources