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());
Related
My React Native application receives a selected image using the react-native-image-picker library and I need to send that image to a back-end running a NestJS server. The endpoint uses #UseInterceptor and FileInterceptor to extract the image from the 'file' field of the formData received. However, when I fire the request to the endpoint, the file received is undefined.
Here is my React Native code sending the request with the file in the FormData as a payload.
const uploadNewProfileImage = async () => {
if (!newProfileImage?.assets) return;
const formData = new FormData();
const profileImage = newProfileImage.assets[0];
console.log(profileImage);
if (profileImage.uri && user) {
formData.append(
'file',
JSON.stringify({
uri:
Platform.OS === 'android'
? profileImage.uri
: profileImage.uri.replace('file://', ''),
name: profileImage.fileName,
type: profileImage.type
})
);
client // client is an Axios instance that injects Bearer Token
.post(`/user/profile/${user.uid}/image`, formData)
.then(({ data }) => {
console.log(data);
})
.catch((err) => {
console.log(err.response);
setShowImageUploadError(true);
})
.finally(() => {
getUserProfile();
});
}
};
Here is my back-end NestJS code extracting the file.
// User.controller.ts
#UseGuards(UserGuard)
#ApiBearerAuth()
#ApiUnauthorizedResponse({ description: 'Unauthorized' })
#UseInterceptors(FileInterceptor('file', { limits: { fileSize: 20000000 } }))
#Post('/profile/:uid/image')
#ApiOkResponse({ type: UploadProfileResponse })
#ApiBadRequestResponse({ description: 'Image too large OR Invalid image type' })
async uploadProfilePicture(#UploadedFile() file: Express.Multer.File, #Request() req): Promise<UploadProfileResponse> {
const uid = req.user.uid;
const imageUrl = await this.userService.uploadProfilePicture(uid, file);
return imageUrl;
}
}
I tried to set the axios request header in the axios config like so
{
headers: {
'Content-Type': 'multipart/form-data; boundary=——file'
}
}
I tried chaning the back-end endpoint to the following
#UseGuards(UserGuard)
#ApiBearerAuth()
#ApiUnauthorizedResponse({ description: 'Unauthorized' })
#UseInterceptors(FileFieldsInterceptor([{ name: 'file' }], { limits: { fileSize: 20000000 } }))
#Post('/profile/:uid/image')
#ApiOkResponse({ type: UploadProfileResponse })
#ApiBadRequestResponse({ description: 'Image too large OR Invalid image type' })
async uploadProfilePicture(#UploadedFiles() file: Array<Express.Multer.File>, #Request() req): Promise<UploadProfileResponse> {
const uid = req.user.uid;
console.log("File", file);
const imageUrl = await this.userService.uploadProfilePicture(uid, file[0]);
return imageUrl;
}
Nothing seems to be working, and the file extracted from the backend is still undefined.
Any help would be greatly appreciated.
I have just switched image upload with Multer from local to Google Cloud Storage using 'multer-google-storage'. It used to work fine earlier, but now sends a 500 Internal Server Error without message. I am using Nodejs and Express, React for front end. FormData is formatted correctly since it works fine if I go back to local storage. Any ideas on how to fix this? Or display an error message? I am not able to find much documentation on 'multer-google-storage'. Thanks for the help!
Here the back-end post route (I hid the configuration options)
const multer = require('multer');
const multerGoogleStorage = require('multer-google-storage');
const upload = multer({
storage: multerGoogleStorage.storageEngine({
autoRetry: true,
bucket: '******',
projectId: '******',
keyFilename: '../server/config/key.json',
filename: (req, file, callback) => {
callback(null, file.originalname);
},
}),
});
//#route POST api/listings
//#description Create listing
//#access Private
router.post(
'/',
upload.any(),
[
isLoggedIn,
[
check('title', 'Title is required').not().isEmpty(),
check('coordinates').not().isEmpty(),
check('address').not().isEmpty(),
check('price', 'Set a price').not().isEmpty(),
check('description', 'Type a description').not().isEmpty(),
check('condition', 'Declare the condition').not().isEmpty(),
check('category', 'Please select a category').not().isEmpty(),
],
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
console.log('validation error');
return res.status(400).json({ errors: errors.array() });
}
try {
const files = req.files;
let images = [];
for (let image of files) {
images.push(image.originalname);
}
const newListing = new Listing({
title: req.body.title,
images: images,
coordinates: JSON.parse(req.body.coordinates),
price: req.body.price,
description: req.body.description,
condition: req.body.condition,
dimensions: req.body.dimensions,
quantity: req.body.quantity,
address: req.body.address,
author: req.user.id,
category: JSON.parse(req.body.category),
});
const author = await User.findById(req.user.id);
await author.listings.push(newListing);
await author.save();
const listing = await newListing.save();
res.json(listing);
} catch (error) {
console.log('error');
console.error(error);
res.json(error);
res.status(500).send('Server Error');
}
}
);
I have solved the issue, it was a permission problem. My previous Google Cloud Storage bucket had access control 'Uniform' while it should have been 'Fine-grained'.
I try to post images to MongoDB but get status CODE 404,
I have multer and static path '/uploads/' and this directory is on the frontend with some images, I get the array from the server but i dont know how to show them :
i tried in flat list to show them like:
<Image source={require("../../server/uploads/a3.jpg")} />//here its work.
and when i replace to {require("../../server/${item.imageurl}" iI get error
when i console log the require path I get the objects with url->> ../../server/uploads\a1.jpg
see that the / is the opposite side \ , maybe its the problem?
now for upload I tried to :
I getting object(selectedImage) when I choosing file :
"localUri": "file:/data/user/0/host.exp.exponent/cache/ExperienceData/%2540anonymous%252FLior-
3698f362-fc81-4a77-8c97-8cd1349ce090/ImagePicker/d4c4b4ec-72e6-4e20-b6eb-4727bc84d93e.jpg",
and my axios post:
if (selectedImage !== null) {
let formData = new FormData();
formData.append("image", selectedImage);
console.log(formData);
try {
const response = await indexApi.post(`/uploads`, formData);
console.log("res", response);
} catch (err) {
console.log("c", err);
}
}
};
the form data that i send look like:
FormData {
"_parts": Array [
Array [
"image",
Object {
"localUri":
"file:/data/user/0/host.exp.exponent/cache/ExperienceData/%2540anonymous%252FLior-3698f362-fc81-
4a77-8c97-8cd1349ce090/ImagePicker/d4c4b4ec-72e6-4e20-b6eb-4727bc84d93e.jpg",
},
],
],
}
and I get error 404, or 503.....
the backend parts:
image controller:
const Image = require("../models/image");
const _ = require("lodash");
exports.getImages = (req, res) => {
Image.find()
.select("_id image desc")
.then(images => {
res.json({ images });
})
.catch(err => console.log("get images errors", err));
};
//INSERT NEW IMAGE//
exports.uploadImage = (req, res) => {
const image = new Image({
image: req.file.path,
desc: req.body.desc,
});
console.log(image);
console.log(image);
console.log("ss", image);
image.save(err => {
if (err) {
return res.status(400).json({ error: "העלאת תמונה נכשלה" });
}
res.json({ message: "העלאת תמונה עברה בהצלחה" });
});
};
image route:
const express = require("express");
const { getImages, uploadImage } = require("../controllers/image");
const multer = require("multer");
//configure the images
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "./uploads");
},
filename: function (req, file, cb) {
console.log("f", file);
cb(null, file.originalname);
},
});
const upload = multer({
storage: storage,
limits: {
fileSize: 1024 * 1024 * 55,
},
});
const router = express.Router();
router.get("/images", getImages);
router.post("/images/new", upload.single("image"), uploadImage);
module.exports = router;
so maybe my prolbem go from the route... beacuse i dont understand the that thing that i have one route (uploads) for the images, and another route with get and post method(/images) and for post (/images/new)
Edit. Error Picture for the first part
Try fixing this first:-
a. Your require might not able to read the incoming value from item.imageUrl since you called it inside double quotes (""):-
try using backticks (``):-
<Image source={require(`../../server/${item.imageUrl}`)} />
OR
You could try one of these two ways:-
saving image path in db
in imageUpload method/function:-
//INSERT NEW IMAGE//
exports.uploadImage = (req, res) => {
const image = new Image({
// don't do this
// image: req.file.path,
// instead try do this
image: '/uploads/' + req.file.originalname,
desc: req.body.desc,
});
console.log(image);
console.log(image);
console.log("ss", image);
image.save(err => {
if (err) {
return res.status(400).json({ error: "העלאת תמונה נכשלה" });
}
res.json({ message: "העלאת תמונה עברה בהצלחה" });
});
saving image originalname only in db
in imageUpload method/function:-
//INSERT NEW IMAGE//
exports.uploadImage = (req, res) => {
const image = new Image({
// don't do this
// image: req.file.path,
// instead try do this
image: req.file.originalname,
desc: req.body.desc,
});
console.log(image);
console.log(image);
console.log("ss", image);
image.save(err => {
if (err) {
return res.status(400).json({ error: "העלאת תמונה נכשלה" });
}
res.json({ message: "העלאת תמונה עברה בהצלחה" });
});
in 'view' or front-end:-
<Image source={require(`../../server/uploads/${item.imageUrl}`)} />
I'm having issues uploading an image from Nativescript to AWS, and I'm pretty sure it's a configuration issue.
select an image
const context = imagepicker.create({
mode: 'single' // use "multiple" for multiple selection
});
await context.authorize();
const selection: Array<ImageAsset> = await context.present();
const imageAsset = selection[0];
const source: ImageSource = await new ImageSource().fromAsset(imageAsset);
const fileLocation = imageAsset.android ? imageAsset.android : imageAsset.ios;
const fileType = mime.extension(mime.lookup(fileLocation));
const image = source.toBase64String(fileType);
console.log(image);
image at this point is: iVBORw0KGgoAAAANSUhEUgAAB4AAAASwCAIAAACVUsChAAAAA3NCSVQI...
image location at this point is: /storage/emulated/0/DCIM/Screenshots/Screenshot_20181106-150854.png
const fileLocation = imageAsset.android ? imageAsset.android : imageAsset.ios;
const signedUrl = await this.getSignedUrl(fileLocation);
Backend Code to get signedURL
const getSignedUrlPromise = (operation, params) => {
return new Promise((resolve, reject) => {
s3.getSignedUrl(operation, params, (err, url) => {
err ? reject(err) : resolve(url);
});
});
}
const params = {
Bucket: BUCKET_NAME,
Key: `abc123/456/3/${fileName}`,
ContentType: contentType,
ContentEncoding: 'base64'
}
const url = await getSignedUrlPromise('putObject', params).catch(err => {
console.log('error', JSON.stringify(err))
return {
statusCode: 400,
body: JSON.stringify(err)
}
});
console.log('success', url);
return {
statusCode: 200,
body: JSON.stringify({ url: url })
}
signedUrl at this point is:
https://myproject.s3.amazonaws.com/abc123/456/3/Screenshot_20181106-150854.png?AWSAccessKeyId=xxxxxxxxxxxxxx&Content-Encoding=base64&Content-Type=image%2Fpng&Expires=1555517358&Signature=yyyyyyyy&x-amz-security-token=long_token
Then, using the signedURL, i upload the image:
const mimeType = mime.lookup(fileLocation);
this.http.put(signedUrl, image, {
headers: {
'Content-Type': mimeType,
'Content-Encoding': 'base64'
}
}).subscribe((resp) => {
console.log('resp2', resp);
});
}
When I open the file, this is what I see
and the meta-data on the S3 object looks correct
When I download the file and open it in NP++, I see the base64 value.
iVBORw0KGgoAAAANSUhEUgAAB4AAAASwCAIAAACVUsChAAAAA3NCSVQI...
I also cannot open the downloaded image
ATTEMPT 2
I saw where some people were using buffers, so I changed my image code to
const image = Buffer.from(source.toBase64String(fileType).replace(/^data:image\/\w+;base64,/, ''), 'base64');
which the image is still broken, and when I download and open the file using NP++ I see
{"type":"Buffer","data":[137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,7,128,0,0,4,176,8,2,0,0,0
React Native IOS application, want to upload image; from device.
RN 0.39.2
Client:
const formData = new FormData()
formData.append('files', file)
formData.append('type', 'image')
fetch(API_HOST+UPLOAD_AVATAR,{
method:'post',
headers: {'Content-Type':'multipart/form-data;boundary=6ff46e0b6b5148d984f148b6542e5a5d','Authorization': 'Bearer'+' '+token},
body: formData
})
.then(response=>response.json())
.then(data=>{
//console.log(data)
//Alert.alert(data)
})
.catch(error=>{
console.log(error)
})
Server :
var multer = require('multer');
var upload = multer();
router.post('/user', ensureAuthenticated, upload.any(), function (req, res) {
console.log(req.body);
console.log(req.files);
})
Error:
server req.body and req.files are empty.
Then I try to use RNFetchBlob.
RNFetchBlob.fetch('POST', API_HOST+UPLOAD_AVATAR, {
'Content-Type':'multipart/form-data;boundary=6ff46e0b6b5148d984f148b6542e5a5d'
'Authorization' : 'Bearer'+' '+token
}, formData)
.then((resp) => {
}).catch((err) => {
// ...
})
then error change to
NSMutableDictionary cannot be converted to NSString.
And req.body is {}, req.files is undefined
I assume you found a solution to this, if yes, could you share it?.
In any case, for the RNFetchBlob issue, I used to get the same error and I solved by changing FormData to an array. Like this:
const body = [{
name: 'data',
data: JSON.stringify(whateverData)
}, {
name: 'file',
data: filePath,
}];
…
RNFetchBlob.fetch('POST', apiEndpoint, headers, body);
Hope that helps.