So i'm building a marketplace mobile app using, expo that let users upload products to the marketplace. I'm having difficult times using the expo FileSystem.FileSystemUploadType(https://docs.expo.dev/versions/latest/sdk/filesystem/#filesystemfilesystemsessiontype) to pick the image and send to the backend.
here's my front-end code
const handleSubmit = async (listing, { resetForm }) => {
const data = new FormData();
data.append("images", listing.images[0]);
data.append("description", "Good product");
console.log( listing.images[0])
// it does console the file uri
// file:///Users/zakisb/Library/Developer/CoreSimulator/Devices/83E12EA5-E8FA-4850-82C1-84021B25450D/data/Containers/Data/Application/6873BF40-26E4-4BD3-834D-F6772448C004/Library/Caches/ExponentExperienceData/%2540anonymous%252Flokazz_app2-5f4724db-b9d7-45aa-a8ca-ac5acf2f4780/ImagePicker/B72CF40C-EC27-430E-B1F8-B983C0ACF2FB.jpg
// i tried this solution first and worked perfectly. but it does upload the image only and i want to send the formdata object
const response = await FileSystem.uploadAsync(
"http://192.168.43.8:5000/products/addProduct",
listing.images[0],
{
fieldName: "images",
uploadType: FileSystem.FileSystemUploadType.MULTIPART,
}
);
// so i tried this but i get an error
const response = await FileSystem.FileSystemUploadType.MULTIPART(
"http://192.168.43.8:5000/products/addProduct",
data
);
};
My backend works perfectly. i tried the api call using insomnia and postman the file gets uploaded successfully to the folder. but using expo methods i get nothing.
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "./images");
},
filename: function (req, file, cb) {
cb(null, Date.now() + file.originalname);
},
router.post(
"/products/addProduct",
upload.single("images"),
async (req, res) => {
console.log(req.body);
try {
res.send(req.body);
} catch (err) {
res.send("Error " + err);
}
}
);
});
Related
I have an API and I'm uploading images using multer. I built backend that works perfectly fine and my images are uploaded and stored in my folder when I use postman, but when I try to upload images using frontend i dont know how to send them. I'm trying to have formData and append my files and then put that in my req.body. I need to have fields with name 'photos' but when i put my data and log req.body on backend i get data: [object FormData] and photos as an empty array. Also when i log req.files i get an empty array. My photos after extracting values from them look like this [File, File]
const handleHotelSubmit = async (e) => {
e.preventDefault();
const data = new FormData();
Object.values(photos).map((photo) => data.append("photos", photo));
setIsLoading(true);
try {
await axios.post(
`/hotels`,
{ ...info, data, featured, rooms },
{
headers: {
"Content-Type": "multipart/form-data",
},
}
);
} catch (err) {
setError(err.message);
}
setIsLoading(false);
};
My multer
const multerStorage = multer.memoryStorage();
const multerFilter = (req, file, cb) => {
if (file.mimetype.startsWith("image")) {
cb(null, true);
} else {
cb(new AppError("Not an image. Please upload only images", 400), false);
}
};
exports.resizeImage = catchAsync(async (req, res, next) => {
console.log(req.files);
if (!req.files) return next();
req.body.photos = [];
await Promise.all(
req.files.map(async (file) => {
const filename = `hotel-${uuidv4()}-${Date.now()}.jpeg`;
await sharp(file.buffer)
.resize(500, 500)
.toFormat("jpeg")
.jpeg({ quality: 90 })
.toFile(`public/img/hotels/${filename}`);
req.body.photos.push(filename);
})
);
next();
});
const upload = multer({
storage: multerStorage,
fileFilter: multerFilter,
});
exports.uploadHotelPhotos = upload.array("photos", 5);
Again code works with postman so clearly the problem is in the frontend
Since you specified in the headers that the request body will be multipart/form-data, then you need to put all other fields (info, featured, rooms) inside the formData data variable
So i've been trying to wrap my head around uploading an image to backend node.js server, but i'm having trouble understanding the doc and the process. Note that i've already done this in a web app before using react in the front and multer in the backend. done it multiple times without trouble. I've found the expo-file-system package which is helpful.
https://docs.expo.dev/versions/v45.0.0/sdk/filesystem/#filesystemuploadasyncurl-fileuri-options
i'm having trouble understanding the FileSystem.uploadAsync method. Here's all code listed down below. a normal axios api call to upload the image would look like this
const data = new FormData();
data.append("images", listing.images[0]);
await axios({
method: "post",
url: "http://localhost:5000/products/addProduct",
data: data,
headers: { "Content-Type": "multipart/form-data" },
})
.then((response) => {
console.log(response);
})
.catch((error) => {
//
});
the backend
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "./images");
},
filename: function (req, file, cb) {
cb(null, Date.now() + file.originalname);
},
});
router.post(
"/products/addProduct",
upload.single("images"),
async (req, res) => {
try {
res.send("ok");
} catch (err) {
res.send("Error " + err);
}
}
);
Also, i tested the backend using an api client (insomnia) it works without a problem !
So please how can i use the FileSytem.uploadAsync method to upload my images to the backend !!!
I'm using Node JS / Express and would like to download a file from url to local system, and in the next step upload it Google Cloud Storage.
This is my router with middlewares:
router.post("", fileFromUrl, uploadFromUrl, scrapeController.scrapeCreateOne);
this is a fileFromUrl middleware that is just saving a file from url to local disk
module.exports = (req, res, next) => {
try {
console.log('Image: ', req.body.image);
const url = req.body.image ? req.body.image : '';
console.log(typeof url);
if(url === '') {
//no image url provided
console.log('image parsing skipped');
next()
}
else {
// image url ok then
const pathToImage = path.resolve(__dirname, '..', 'images', Date.now() + '_file.jpg');
const localPath = fs.createWriteStream(pathToImage);
const saveFile = https.get(url, (response) => {
console.log(response.headers['content-type']);
response.pipe(localPath);
})
req.body.customImageUrl = pathToImage;
req.body.customImageName = path.basename(pathToImage);
next();
}
}
catch (error) {
console.log(error)
}
}
this is uploadFromUrl middleware that should upload the file from local path to the Google Cloud Storage
module.exports = (req, res, next) => {
try {
console.log(req.body.customImageUrl);
console.log(req.body.customImageName);
//storage.getBuckets().then(x => console.log(x));
//storage.createBucket(`testbucket_${Date.now()}`); / // it does work
storage.bucket(bucketName).upload(req.body.customImageUrl, {
gzip: true,
metadata: {
cacheControl: 'public, max-age=31536000',
},
}).then(
req.body.customData = `https://storage.googleapis.com/${bucketName}/${req.body.customImageName}`
);
next();
}
catch (error) {
res.send(error).json({
message: 'Error upload middleware' + error,
});
}
}
What is does right now, is just uploading almost empty file with 20kB to the Google Cloud Platform, not the full image. I feel that I'm not providing a proper file object to the uploadFromUrl middleware. On the other hand GCP API to upload a file is just asking for the path to the file which is being provided. Any ideas ?
The issue was that I was trying to upload image to GCP, even though the image was not yet fully saved on the server. The solution was to wait for 'finish' event in the request I made to save it locally
const pathToImage = path.resolve(__dirname, '..', 'images', Date.now() + '_file.jpg');
const localPath = fs.createWriteStream(pathToImage);
const fileRelativePath = "images/" + path.basename(pathToImage);
const request = https.get(url, (response) => {
//console.log(response.headers['content-type']);
response.pipe(localPath).on('finish', () => {
console.log('image saved on the server');
req.body.customImagePath = fileRelativePath;
req.body.customImageName = path.basename(pathToImage);
// now you can go and upload the image to GCP in the next moddleware
next();
});
});
I am new in Node js API and I'm trying to upload an Image using multer + express + mongoose + postman
CODE:
var storage = multer.diskStorage({
destination: function (request, file, callback) {
callback(null, 'public/images/course');
},
filename: function (request, file, callback) {
return callback(null, file.originalname)
}
});
var upload = multer({storage : storage})
router.post('/course', upload.single('thumbnail'),async(req, res) => {
try{
var course = new Course({
name : req.body.name,
thumbnail : "placeholder" // set to path where file is uploaded
})
await course.save()
res.status(201).send(course)
}catch(e){
res.status(400).send(e)
}
})
I use postman to post request using form data and it creates an image with its originalFilename but i want the filename to be id generated by mongoose and i have seen somewhere that i can use filesystem for that but is there any way i can upload file after id is generated because when i do like this
var storage = multer.diskStorage({
destination: function (request, file, callback) {
callback(null, 'public/images/course');
},
filename: function (request, file, callback) {
if (request.data) {
console.log(file)
// TODO: consider adding file type extension
fileExtension = file.originalname.split('.')[1]
return callback(null, `${request.path}-${request.data._id.toString()}.${fileExtension}`);
}
// fallback to the original name if you don't have a book attached to the request yet.
return callback(null, file.originalname)
}
});
var upload = multer({storage : storage}).single('thumbnail')
router.post('/course',(req, res) => {
console.log(req.body)
const course = new Course({
name : req.body.name,
thumbnail : req.body.name
})
//console.log(course)
req.data = course
//console.log(req.file)
upload(req, res, function (err) {
if (err instanceof multer.MulterError) {
// A Multer error occurred when uploading.
} else if (err) {
// An unknown error occurred when uploading.
}
// Everything went fine.
})
})
Then i got request body empty.
Thanks in advance
I'm trying to set user to have one image upload. This is the code I have in the router
const multer = require('multer')
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/')
},
filename: function (req, file, cb) {
cb(null, file.filename + '-' + Date.now() + '.jpg')
}
})
const upload = multer({ storage: storage }).single('image')
appRouter.post('/upload', async (req, res) => {
try {
const uploadImage = await Image.create(req.body)
upload(req, res, () => {
res.json({
uploadImage,
success: true,
message: 'Image uploaded'
})
})
} catch (err) {
console.log(err)
}
})
and heres how I have it in model
module.exports = (db, Sequelize) => {
return db.define('image', {})
}
and this as well.
const Image = imageModel(db, Sequelize)
User.hasOne(Image)
This is a fullstack express app with auth and I'm trying to enable users to upload their own images.
Heres how it looks on postman, userId is still null:
You are never associating the image with an user.
You are creating the image, but don't add a user to the image. You need a way to identify the user and then associate it when creating (or after creating the image).
You'll probably need to implement way for users to identify themselves to do this.
Your model declaration implies that a User has an image, this doesn't mean that all images have an user, there may be images without users.