When I drop a file in the upload area, the React-dropzone returns an object such as:
let picture = [
{
"rawFile": {
"preview": "blob:http://localhost:3000/ff851b03-b2c0-4212-9240-8d07057ad47d"
},
"src": "blob:http://localhost:3000/ff851b03-b2c0-4212-9240-8d07057ad47d",
"title": "1397-01-20 13.43.24.jpg"
}
]
I read this link and try to upload the file: React dropzone, how to upload image?
But I think the file will not be sent.
This is my code:
let formData = new FormData();
formData.append('file', picture[0]);
fetch('http://localhost:8000/api/media', {
method: 'POST',
body: formData
});
If this method is not correct, How to send the file to the server side and receive it on the server side?
On the server side, I'm using Hapij.
I solved the problem. I write the answer because anybody didn't answer this question.
In the client side, I use the FileReader API to read the BLOB data and convert it to base64 readable format. I write a function to convert blob to base64 and send fileName and base64 to the server side.
const convertFileToBase64 = file => new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file.rawFile);
reader.onload = () => resolve({
fileName: file.title,
base64: reader.result
});
reader.onerror = reject;
});
On the server side, I write the buffer to the file by this function:
const fs = require("fs");
const Boom = require('boom');
function convertBase64ToFile(file) {
let base64Data = file.base64.split(',')[1];
fs.writeFile(`${__dirname}/../../uploads/${file.fileName}`, base64Data, 'base64', function(err) {
return Boom.badData(err);
});
// Other actions...
}
This method works for me perfectly.
Related
When attempting to upload a file to Amazon S3 using axios, I have been encountering a very strange issue. Normally, in a web browser, when FormData has binary data in it, the Content-Type header automatically gets set to multipart/form-data; boundary=<some random string>. However, I have been completely unable to achieve that in React Native (testing on an iOS device). The Content-Type is automatically set to application/json, and thus not being detected as a correctly formatted body when uploading to Amazon S3. I have tried specifying a blob in the file parameter in FormData instead of the URI to the file as well to no avail. I have appended my code below, any advice would be very much appreciated.
const uploadFileToS3 = (
presignedPostData,
file) => {
// create a form obj
const formData = new FormData();
// append the fields in presignedPostData in formData
Object.keys(presignedPostData.fields).forEach(
key => {
formData.append(
key,
presignedPostData.fields[key],
);
},
);
// append the file and uplaod
const getBlob = async () => {
const img_url = previewPath;
let result = await fetch(img_url);
const blob = await result.blob();
formData.append('Content-Type', 'image/jpeg');
formData.append('file', {
uri: previewPath,
type: 'image/jpeg',
name: 'test.jpeg',
});
console.log(formData, 'wild');
// post the data on the s3 url
axios
.post(presignedPostData.url, formData)
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error.response);
});
};
getBlob();
};
I was successful in storing images in mongodb using the base64 url. But when I tried to do so with PDFs, it gave an url which does not work. Wait, let me explain, when I put the image base64 url in the req.body of the POST request, the special signs would get disappeared, so I tried encodeURIComponent() method to make it error free. After that I found that storing the huge string in mongodb was too short to fit in the db, so I tried: app.use(express.json({limit: '50mb'})); app.use(express.urlencoded({limit: '50mb', extended: false })); and It worked! but when the client requests the base64 url, it would come encoded, so I put decodeURIComponent() to decode it and was not a great issue nd I got the desired result, yet with the Image one.
The main issue issue is when it comes to PDF. I don't know why it's happening with PDF only! when I make base64 url in CLIENT side and test it, it works fine, but when it comes to server side, all the mess happens. please help me deal with this.
Note: "I don't want to use Gridfs, formidabe, multer etc for file things"
here's my piece of code:
$('#seasonForm').submit(async function (e) {
e.preventDefault();
const form = $(this);
const ImgFile = document.getElementById('seasonThumbnail').files[0];
const PDFFile = document.getElementById('seasonPDF').files[0];
const imgurl = encodeURIComponent(await getBase64(ImgFile));
const PDFurl = encodeURIComponent(await getBase64(PDFFile));
const url = '/uploadSeason';
$.ajax({
type: "POST",
url: url,
data: form.serialize()+`&Version=<%- NxtSeasons %>&image=${imgurl}&PDF=${PDFurl}`,
success: data => {
console.log(data.message);
if (data.status == "error") {
showIt(".alert", data.message, "error");
} else {
showIt(".alert", data.message, "success");
}
}
});
})
wait, now don't get confused with getBase64() and showIt. these are my functions. getBase64() is a promice which returns base64 url of the file and showIt() is type of alert which I made. Now if you don't know what is base64 url, this is the getBase64 one's code:
const getBase64 = (file) => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = error => reject(error);
});
}
My nodejs code:
app.post("/uploadSeason", admin, async (req, res) => {
try {
const { Name, Desctiption, Version, image, PDF } = req.body;
const newSeason = new Season({
Name,
Desctiption,
Version,
image: encodeURIComponent(image),
PDF: encodeURIComponent(PDF),
});
await newSeason.save();
res.json({
status: "success",
message: "Season added successfully"
});
} catch (e) {
console.log(e);
res.json({
status: "error",
message: e
});
}
});
I have tried to send a binary file with additional fields using post method and multipart/form-data.
load() {
const formData = new FormData();
formData.append('file', this.data.binary);
formData.append('reasons', this.form.get('reasons').value);
const headers = new HttpHeaders({
key: userKey,
});
const options = { headers: headers };
this.http
.post(`/api/Controller`, formData, options)
.pipe(catchError(this.handleError))
.subscribe(
() => {
alert('Success');
},
(error) => {
alert(error.Message || 'Error');
},
);
}
Where this.data.binary is:
try {
const input = event.target as HTMLInputElement;
const reader = new FileReader();
reader.onload = () => {
this.data.binary = reader.result;
};
reader.readAsArrayBuffer(input.files[0]);
} catch (e) {
alert(e);
}
In Chrome headers I see this data after submit:
FormData
file: [object ArrayBuffer]
reasons: text
How to send file as binary correct?
I have trird also this:
formData.append('file', new Blob(this.data.binary));
don't use the FileReader to read it as binary.
Just use the FormData and append it directly
const formData = new FormData()
formData.append('file', input.files[0])
beside, readAsBinaryString is a bad practices and should use arrayBuffer instead when dealing with binary
I would also suggest that you do something like new FormData(formElement) so that you don't have to append/set every single field that is needed.
I pass image data by appending with formdata in reactjs
This is the handler for this
handleUserImage(e) {
const input = e.target
if (input.files && input.files[0]) {
const reader = new FileReader()
reader.onload = (r) => {
const formdata = new FormData()
formdata.append('photos', input.files[0])
formdata.append('fileName', input.files[0].name)
this.props.uploadImage({ image: formdata })
}
reader.readAsDataURL(input.files[0])
}
}
and when I console it to backend it looks like blank object
{ image: {} }
Then how can I pass formdata and upload the image
Edit : Upload image api call
export const uploadImage = (data) => {
console.log(data)
return fetch(`http://hostName:3001/town/image`, {
method: 'POST',
headers: {
'Content-Type': 'multipart/form-data'
},
body: data
})
.then((res) => {
return res.json()
})
.then((payload) => {
console.log(payload)
return payload
}).catch((error) => {
throw error
})
}
when I console it to backend it looks like blank object
This is normal.
headers: {
'Content-Type': 'multipart/form-data'
},
Multipart form data MIME types need to have a parameter to describe the boundary marker (so parsers know where one field ends and the next begins).
Yours is missing it. What's more, there is no way for you to know what it will be.
Don't override the Content-Type header, allow fetch to generate it from the FormData object. Remove the code I quoted entirely.
Why use FileReader, upload it as base64 and append file name separately?
you can just do
handleUserImage (evt) {
const file = evt.target.files[0]
if (file) {
const formdata = new FormData()
formdata.append('photos', file)
this.props.uploadImage({ image: formdata })
}
}
You save a few bytes,
avoid unnecessary decode & encode calculations
runs faster
saves ~3x bandwidth
FormData.append(name, value, filename)
name
The name of the field whose data is contained in value.
value
The field's value. This can be a USVString or Blob (including subclasses such as File).
filename Optional
The filename reported to the server (a USVString), when a Blob or File is passed as the second parameter. The default filename for Blob objects is "blob". The default filename for File objects is the file's filename.
-- MDN - FormData.append()
Using Node.js, I am trying to get an image from a URL and upload that image to another service without saving image to disk. I have the following code that works when saving the file to disk and using fs to create a readablestream. But as I am doing this as a cron job on a read-only file system (webtask.io) I'd want to achieve the same result without saving the file to disk temporarily. Shouldn't that be possible?
request(image.Url)
.pipe(
fs
.createWriteStream(image.Id)
.on('finish', () => {
client.assets
.upload('image', fs.createReadStream(image.Id))
.then(imageAsset => {
resolve(imageAsset)
})
})
)
Do you have any suggestions of how to achieve this without saving the file to disk? The upload client will take the following
client.asset.upload(type: 'file' | image', body: File | Blob | Buffer | NodeStream, options = {}): Promise<AssetDocument>
Thanks!
How about passing the buffer down to the upload function? Since as per your statement it'll accept a buffer.
As a side note... This will keep it in memory for the duration of the method execution, so if you call this numerous times you might run out of resources.
request.get(url, function (res) {
var data = [];
res.on('data', function(chunk) {
data.push(chunk);
}).on('end', function() {
var buffer = Buffer.concat(data);
// Pass the buffer
client.asset.upload(type: 'buffer', body: buffer);
});
});
I tried some various libraries and it turns out that node-fetch provides a way to return a buffer. So this code works:
fetch(image.Url)
.then(res => res.buffer())
.then(buffer => client.assets
.upload('image', buffer, {filename: image.Id}))
.then(imageAsset => {
resolve(imageAsset)
})
well I know it has been a few years since the question was originally asked, but I have encountered this problem now, and since I didn't find an answer with a comprehensive example I made one myself.
i'm assuming that the file path is a valid URL and that the end of it is the file name, I need to pass an apikey to this API endpoint, and a successful upload sends me back a response with a token.
I'm using node-fetch and form-data as dependencies.
const fetch = require('node-fetch');
const FormData = require('form-data');
const secretKey = 'secretKey';
const downloadAndUploadFile = async (filePath) => {
const fileName = new URL(filePath).pathname.split("/").pop();
const endpoint = `the-upload-endpoint-url`;
const formData = new FormData();
let jsonResponse = null;
try {
const download = await fetch(filePath);
const buffer = await download.buffer();
if (!buffer) {
console.log('file not found', filePath);
return null;
}
formData.append('file', buffer, fileName);
const response = await fetch(endpoint, {
method: 'POST', body: formData, headers: {
...formData.getHeaders(),
"Authorization": `Bearer ${secretKey}`,
},
});
jsonResponse = await response.json();
} catch (error) {
console.log('error on file upload', error);
}
return jsonResponse ? jsonResponse.token : null;
}