How to upload images from react native to an express backend? - express

I followed this example to upload images to my backend. https://github.com/expo/image-upload-example
The code I use in my RN application is:
let formData = new FormData();
formData.append('name',this.state.name);
formData.append('description',this.state.description);
formData.append('price',this.state.price);
formData.append('oprice',this.state.oprice);
let fileArr = (this.state.image).split('.');
let type = fileArr[fileArr.length -1]
let uri = this.state.image
formData.append('photo',{uri, name: `${this.state.name}.${type}`, type: `image/${type}`});
console.log(formData);
AsyncStorage.getItem('jwt', (err, token) => {
fetch('http://192.168.1.83:8000/ShopRouter/deal', {
method: 'POST',
headers: {
Accept: 'application/json',
Authorization: token,
'Content-type': false
},
body: formData
})
The code I use for my express backend is:
app.post('/ShopRouter/deal', passport.authenticate('jwt2', {session:false}), function(req,res) {
upload(req, res, function(err) {
if (err) {
console.log(err);
}
console.log(req.file);
console.log(req.files);
console.log(req.photo);
console.log(req.body.name);
console.log(req.body.description);
My Multer configuration is:
var storage = multer.diskStorage({
destination: function (req, file, cb, err){
console.log(file)
if (err){
console.log(err)
}
cb(null, './uploads/')
},
filename: function (req, file, cb) {
console.log(file)
cb(null, file.originalname + '-' +Date.now())
}
});
var upload = multer({ storage: storage }).single('photo');
The line console.log(file) prints
{ fieldname: 'photo',
originalname: 'An empty bottle .jpg',
encoding: '7bit',
mimetype: 'image/jpeg' }
I;m not sure why the backend receives this without the uri of the image, nothing gets saved in the upload folder
req.file comes back undefined.

I sorted this out by just uploading the images to s3

Related

uploading files to endpoint from a static webpage

I am trying to upload files to an S3 endpoint from a static HTML page but the files are always malformed when I download them from the bucket. The relevant code is below - what am I doing wrong with fetch?
const onSubmitForm = function (e) {
const file = this.files[0];
const reader = new FileReader();
// reader.readAsText(file); // didn't work
reader.readAsDataURL(file); // also didn't work
reader.onload = async function () {
const bodyData = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
file: {
// the backend endpoint expects a base64 encoded img
// the upload completes but the
"data": reader.result.toString()
},
"name": file.name
})
}
const response = await fetch(uploadUrl, bodyData).then(
res => res.json()
);
};
reader.onerror = function (error) {
console.log('Error: ', error);
};
//
}

Cloudinary\Error: Missing required parameter - file - Express and Postman

first time trying to upload images to Cloudinary and I have come across an issue when using Express via Postman.
Using form-data on setting 'file' to upload an image to Cloudinary
As of now, when I try to access the req.body I get an empty object, so I guess that has to do with why cloudinary.uploader.upload cannot read the file passed as its first param, since its req.body.file, as shown in the code below.
cloudinary.config({
cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
api_key: process.env.CLOUDINARY_KEY,
api_secret: process.env.CLOUDINARY_SECRET
})
exports.upload = async (req, res) => {
try{
console.log(req.body);
const result = await cloudinary.uploader.upload(req.body.file, {
public_id: `${Date.now()}`,
resource_type: "auto"
})
return res.json({
public_id: result.public_id,
url: result.secure_url
})
}catch(err){
console.log(err)
}
}
The error message I get:
{
message: 'Missing required parameter - file',
name: 'Error',
http_code: 400
}
Any suggestions to solve this issue?
I solved it! I was not able to pass the form-data as req.body to the server, so I had to try and access it through req.files, but was not able to with that either, so I searched a bit and found a middleware 'express-fileupload', and that did the trick. I just added it in my app.js and used
const fileupload = require('express-fileupload');
app.use(fileupload({useTempFiles: true}))
So now I can access my req.files.
exports.upload = async (req, res) => {
const file = req.files.image
try{
console.log(file);
const result = await cloudinary.uploader.upload(file.tempFilePath, {
public_id: `${Date.now()}`,
resource_type: "auto"
})
res.json({
public_id: result.public_id,
url: result.secure_url
})
}catch(err){
console.log("Error", err)
return res.status(400).json({error: err})
}
}
The response I get is:
{
name: 'some-image.png',
data: <Buffer >,
size: 99770,
encoding: '7bit',
tempFilePath: ' **C:\\filepath\some-image.png** ',
truncated: false,
mimetype: 'image/png',
md5: 'b5f612a571442bf604952fd12c47c1bf',
mv: [Function: mv]
}
POST /cloudinary/upload-images 200 1617.944 ms - 119
And it is uploaded successfully to my Cloudinary.
const result = await cloudinary.uploader.upload(req.file, {
public_id: ${Date.now()},
resource_type: "auto"
})
and add file from form data and type should be File
Solved!
This is how i am setting the FormData
let myTestForm = new FormData();
myTestForm.set("name", name);
myTestForm.set("email", email);
myTestForm.set("Avatar", Avatar);
myTestForm.set("password", password);
This is how i am using the FormData
const config = {
headers: {
"Content-type": "multipart/form-data",
},
};
const { data } = await axios.post(`/api/v1/register`, userData, { config });
please don't pass it this way { userData} , had struggled for with this :/
This is how i am uploading image
const myCloud = await cloudinary.v2.uploader.upload(req.body.Avatar, {
folder: "Avatars",
width: 150,
crop: "scale",
public_id: `${Date.now()}`,
resource_type: "auto",
});
PS : in my case i had to upload only 1 image. Have not passed any parameter in app.js file
app.use(bodyParser.urlencoded({ extended: true }));
app.use(fileUpload());

Passing formData to upload image with nodeJS (Multer) -> 'react-native-image-crop-picker'

I have currently a problem when i want to uploads files from client mobiles devices to my backend (NodeJS) with MULTER.
I'm using 'react-native-image-crop-picker' to pick the images on the devices (from the library or the camera)
I have used formData to make the response as file to the backend but the req file is still undefined.
My front-end Code:
let response = await ImagePicker.openCamera({
height: 400,
width: 400,
cropping: true,
});<br>
let media = {
uri: response.path,
type: response.mime,
name: response.filename,
};<br>
let form = new FormData();
form.append('uploads', media);<br>
if (form !== null) {<br>
await Axios({
method: 'POST',
url: url,
data: form,<br>
headers: {
'Content-Type': 'multipart/form-data',
},
})
My backend :
router.post('/article', upload.single('uploads'), async (req,
res) =>
{<br>
try {
console.log(req.file);
} <br>catch (err) {
console.log(err.message);<br>
res.status(500).send('SERVOR ERROR');
}
});
front-end
back-end
console.log(req.file) = "UNDEFINED"
I have used to try with fs.readFile :
let d = {};
let e = await Fs.readFile(response.path, 'base64');
let form = new FormData();
form.append('uploads', e);
d.uploads = form;
let config = {'Content-Type': 'multipart/form-data'};
Axios.post(url, d, config)
.then((res) => console.log(res))
.catch((err) => console.error(err));
}
The req.file is still undefined but the req.body shows this :
req.file => undefined
req.body => { uploads: { _parts: [ [Array] ] } }

Unexpected end of multipart data nodejs multer s3

iam trying to upload image in s3 this is my code
const upload = require('../services/file_upload');
const singleUpload = upload.single('image');
module.exports.uploadImage = (req,res) => {
singleUpload(req, res, function (err) {
if (err) {
console.log(err);
return res.status(401).send({ errors: [{ title: 'File Upload Error', detail: err}] });
}
console.log(res);
return res.json({ 'imageUrl': req.file.location });
});
}
FileUpload.js
const aws = require('aws-sdk');
const multer = require('multer');
const multerS3 = require('multer-s3');
const s3 = new aws.S3();
const fileFilter = (req, file, cb) => {
if (file.mimetype === 'image/jpeg' || file.mimetype === 'image/png') {
cb(null, true)
} else {
cb(new Error('Invalid Mime Type, only JPEG and PNG'), false);
}
}
const upload = multer({
fileFilter,
storage: multerS3({
s3,
bucket: 'image-bucket',
acl: 'public-read',
contentType: multerS3.AUTO_CONTENT_TYPE,
metadata: function (req, file, cb) {
cb(null, {fieldName: 'TESTING_META_DATA!'});
},
key: function (req, file, cb) {
cb(null,"category_"+Date.now().toString()+".png")
}
})
})
module.exports = upload;
I tried to test api with postmanin serverless local it is giving this error
Error: Unexpected end of multipart data
at D:\Flutter\aws\mishpix_web\node_modules\dicer\lib\Dicer.js:62:28
at process._tickCallback (internal/process/next_tick.js:61:11) storageErrors: []
After deploying online. i tried the api. the file is uploaded in server but its a broken
Are you using aws-serverless-express. aws-serverless-express will forward original request to Buffer as utf8 encoding. So multipart data will be lost or error. I am not sure why.
So, I change aws-serverless-express to aws-serverless-express-binary and everything worked.
yarn add aws-serverless-express-binary
Hope this help!

Serverless Api Gateway Proxy Lambda with Axios to serve binary files (PDF)?

I am using serverless and axios to "passthrough" a PDF generated by the
stampery api, which needs credentials I do not want to save client-side.
In principle I get the PDF as an arraybuffer from axios, transform the arraybuffer to an buffer, which I then use to create a base64 string, needed by API Gateway binary treatment, which can be "activated" by including the isBase64Encoded: true property in the callback response object.
The following code yields an empty PDF:
const axios = require('axios');
const stamperyClientId = 'xxx';
const stamperySecret = 'xxx';
const stampery = axios.create({
baseURL: 'https://api-prod.stampery.com',
auth: {
username: stamperyClientId,
password: stamperySecret
}
});
module.exports.hello = (event, context, callback) => {
const response = {
statusCode: 200,
headers: {
'Content-Type' : 'application/pdf',
'Content-Disposition': 'inline; filename="certificate.pdf"'
},
isBase64Encoded: true
};
stampery.get(`/stamps/5b2a612680e0190004bcccc8.pdf`, {
responseType: 'arrayBuffer',
})
.then(res => {
const buffer = Buffer.from(res.data)
const base64Str = buffer.toString('base64')
response.body = base64Str
callback(null, response);
})
};
The PDF is retrieved by: `curl https://xxx.execute-api.eu-west-1.amazonaws.com/dev/certificate -o my.pdf -H "Accept: application/pdf"
I tested the setup with fileReadSync, which turned out fine:
module.exports.hello = (event, context, callback) => {
const content = fs.readFileSync("data/sample.pdf");
const response = {
statusCode: 200,
headers: {
"Content-Type": "application/pdf",
"Content-Disposition": "inline; filename=\"sample.pdf\""
},
body: content.toString("base64"),
isBase64Encoded: true
};
return callback(null, response);
};
What am I missing? Is this the right way to transform an axios arraybufferinto a base64 string?
P.S. My serverless.yml is given by:
functions:
hello:
handler: handler.hello
events:
- http:
path: certificate.pdf
method: get
package:
include:
- data/sample.pdf
resources:
Resources:
ApiGatewayRestApi:
Type: AWS::ApiGateway::RestApi
Properties:
Name: dev-stamper
BinaryMediaTypes:
- "application/pdf" # or whichever ones you need
where I already thought of the necessary binary media types.
Ok... the error was a simple typo. I wrote arraybuffer in camel case arrayBuffer, so Axios returned a string instead of an arraybuffer.
Change this line:
stampery.get(`/stamps/5b2a612680e0190004bcccc8.pdf`, {
responseType: 'arrayBuffer',
})
to
stampery.get(`/stamps/5b2a612680e0190004bcccc8.pdf`, {
responseType: 'arraybuffer',
})
and everything works as a charm...