I try to resize my image with sharp and multer - javascript

I created a middleware with multer at the backend like this :
const multer = require("multer");
const MIME_TYPES = {
"image/jpg": "jpg",
"image/jpeg": "jpg",
"image/png": "png",
};
const imageFilter = (req, file, cb) => {
if (file.mimetype.startsWith("image")) {
cb(null, true);
} else {
cb("Please select an image file!", false);
}
};
const storage = multer.diskStorage({
destination: (req, file, cb) => {
let dir = `uploads/` + file.fieldname;
if (file.fieldname === "profil") {
cb(null, dir);
} else if (file.fieldname === "category") {
cb(null, dir);
} else {
cb(null, `uploads/food`);
}
},
filename: (req, file, cb) => {
let fileExtension = file.originalname
.split(".")
[file.originalname.split(".").length - 1].toLocaleLowerCase();
let extension = MIME_TYPES[file.mimetype];
file.extension = fileExtension.replace("/jpeg/i", "jpg"); // all jpeg images to jpg
cb(
null,
`${file.originalname.replace(/\W|jpeg|jpg|png/g, "")}.${fileExtension}` // Removes non-words form filename
);
},
});
In my controllers I try to use the sharp-js module to resize the image before saving it in the db and I use req.protocol to display the image on the user's frontend:
/Store the compress img in db
const fileImg = `${req.protocol}://${req.get("host")}/uploads/${
req.file.fieldname
}/${food}`;
const productImg = `${req.protocol}://${req.get("host")}/uploads/${
req.file.fieldname
}/resized_${food}`;
//Verify if product is also stored in DB
const productIsFound = await Product.findOne({ where: { name: name } });
if (productIsFound)
return res
.status(401)
.json(`${name} existe déjà sur la liste des produits`);
//Convert any input to very high quality JPEG
console.log(productImg);
console.log(fileImg);
console.log(file);
await sharp(fileImg)
.resize(640, 427, { fit: "cover" })
.jpeg({
quality: 100,
chromaSubsampling: "4:4:4",
})
.toFile(productImg);
//fs.unlinkSync(fileImg);
//Check if picture fields isn't undefined
if (file !== undefined) {
image = productImg;
}
//Check if admin is connected and the category exist in DB
const product = await Product.create({
categorieId,
name,
product_image: image,
price,
});
and when I run my query with postman this is the error message I get.

Related

How to solve Firebase Storage: Invalid URL when trying to upload and image react native

I am trying to upload an image to firebase storage, the problem is I get Firebase Storage: Invalid URL when I try to upload it.
First I get the uri from the react-native-image-picker then I use it to make the reference.
This is my code:
export async function uploadImage() {
const options = {
storageOptions: {
path: "images",
mediaType: "photo"
},
includeBase64: true
}
const result = await launchImageLibrary(options);
const imagePath = result.assets[0].uri
console.log(imagePath)
//firebase
const imageRef = ref(storage, imagePath);
const snapshot = await uploadBytes(imageRef, file, {
contentType: "image/jpeg",
});
console.log("uploaded!")
}
this is the uri printed by the console.log:
file:///data/user/0/com.chatapp/cache/rn_image_picker_lib_temp_f85b1089-267f-4271-9ccb-2f1487d83619.jpg
while uploading any to firebase storage
you should have permission to upload the file.
what do you want to upload the file or any Base64 content
const uploadImage = async () => {
const options: ImageLibraryOptions = {
storageOptions: {
path: 'images',
mediaType: 'photo',
},
includeBase64: true,
};
const result = await launchImageLibrary(options);
if (result) {
const {assets} = result;
if (assets && assets.length > 0) {
try {
const imagePath = result.assets[0].uri;
console.log(imagePath);
//firebase
const reference = storage().ref('black-t-shirt-sm.png');
const imageRef = await reference.putFile(imagePath, {
contentType: 'image/jpeg',
});
console.log('imageRef', imageRef);
// const snapshot = await uploadBytes(imageRef, file, {
// contentType: 'image/jpeg',
// });
console.log('uploaded!');
} catch (error) {
console.log('error', error);
}
}
}
};
for uploading the file you need to follow its guideline RN Firebase
storage upload requires platform specific code. I have code snippet at my github playground with storage upload and react-native-image-picker Snippet
Essentially
const uri = Platform.OS === 'android' ? uploadUri : uploadUri.replace('file://', '');
const task = storage().ref(ref);
return task.putFile(uri);

file upload backup api to backup file on aws s3 in nodejs

I want a make an API that will take a file or folder path from the user and upload it to AWS s3 I made progress but
when the user gives a file path it's searching the file path in the server, not in the user's pc
I know I made a mistake but I don't know how to connect API from the users pc and get access to system files
here is code for the post route
router.post("/create/:id", auth, async (req, res) => {
try {
let form = new multiparty.Form();
form.parse(req, async (err, fields, files) => {
console.log(fields);
console.log(files);
//check if user has access to project
const user_id = req.userId;
const project_id = req.params.id;
const user_access = await check_user_access_project(user_id, project_id);
const user = await User.findById(user_id);
const project = await Project.findById(project_id);
if (user_access === 1) {
//create version
const version = new Version({
project_id: project_id,
user_id: user_id,
versionName: fields.versionName[0],
version_description: fields.versionDescription[0],
version_file: [],
});
const version_data = await version.save();
console.log(version_data);
let version_id = version_data._id;
//sync folders to s3
const version_folder_path = fields.files_path[0];
let key = `${user.firstName}_${user_id}/${project.projectName}/${fields.versionName[0]}`;
const version_folder_list = await sync_folders(
version_folder_path,
key
);
console.log("version folder list", version_folder_list);
//update version with version folders
await Version.findByIdAndUpdate(
version_id,
{
$set: {
version_file: version_folder_list,
},
},
{ new: true }
);
//wait for version update
await version.save();
//send response
res.json({
success: true,
version: version_data,
});
} else {
res.status(401).json({
success: false,
message: "User does not have access to project",
});
}
});
} catch (error) {
res.status(400).json({ message: error.message });
}
});
here is the folder sync code
const sync_folders = async (folder_path, key) => {
function getFiles(dir, files_) {
files_ = files_ || [];
var files = fs.readdirSync(dir);
for (var i in files) {
var name = dir + "/" + files[i];
if (fs.statSync(name).isDirectory()) {
getFiles(name, files_);
} else {
files_.push(name);
}
}
return files_;
}
const files = getFiles(folder_path);
console.log(files);
const fileData = [];
for (let i = 0; i < files.length; i++) {
const file = files[i];
console.log(file);
const fileName = file.split("/").pop();
const fileType = file.split(".").pop();
const fileSize = fs.statSync(file).size;
const filePath = file;
const fileBuffer = fs.readFileSync(filePath);
//folder is last part of folder path (e.g. /folder1/folder2/folder3)
const folder = folder_path.split("/").pop();
console.log("folder: " + folder);
//split filepath
const filePath_ = filePath.split(folder).pop();
let filekey = key + "/" + folder + filePath_;
console.log("filekey: " + filekey);
const params = {
Bucket: bucket,
Key: filekey,
Body: fileBuffer,
ContentType: fileType,
ContentLength: fileSize,
};
const data = await s3.upload(params).promise();
console.log(data);
fileData.push(data);
}
console.log("file data", fileData);
console.log("files uploaded");
return fileData;
};
if some buddy can help me pls I need your help
You need to post the item in a form rather than just putting the directory path of user in and then upload the result to your s3 bucket.
This might be a good start if you're new to it:
https://www.w3schools.com/nodejs/nodejs_uploadfiles.asp

Try to upload an image into firebase storage

i'm trying to upload and image from a device to firebase storage but i don't know which format i should use. i've try with put and putString, but both of them gave me invalid argument.
This is the code to pick and upload the image.
const pickImage = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
});
if (!result.cancelled) {
setImage(result.uri);
}
};
const uploadImage = async () => {
if (!image) {
Alert.alert(
'You have to choose an image first'
);
} else {
const uri = image;
console.log(uri);
const filename = uri.substring(uri.lastIndexOf('/') + 1);
const uploadUri = Platform.OS === 'ios' ? uri.replace('file://', '') : uri;
setUploading(true);
setTransferred(0);
const task = firebase.storage()
.ref(filename)
.put(uploadUri);
// set progress state
task.on('state_changed', snapshot => {
setTransferred(
Math.round(snapshot.bytesTransferred / snapshot.totalBytes) * 10000
);
});
try {
await task;
} catch (e) {
console.error(e);
}
setUploading(false);
Alert.alert(
'Photo uploaded!',
'Your photo has been uploaded to Firebase Cloud Storage!'
);
setImage(null);
}
};
This is the uri of the image (the console.log output) :
This is the error using .put(uploadUri):
This is the error using .putString(uploadUri, 'data_url') :
In order to upload an image on Firebase storage using put you need to pass a blob as param instead of string.
For example you can do something like this:
import path from 'path'
const uid = 'image-id'
const fileName = uid + path.extname(uri)
const response = await fetch(uri)
const blob = await response.blob()
const uploadImage = firebase
.storage()
.ref()
.put(blob, {
contentType: `image/${path.extname(uri).split('.').pop()}`
})
uploadImage.on(
'state_changed',
snapshot => {
// progress
},
err => {
// error
},
() => {
// complete
}

Upload directory based on the form fields data in formidable?

When uploading files (images for a Project entity) I would like to create a new Linux subdirectory in /public/images for each Project ID to store its images in. However the images are saved immediately into the directory provided before I can specify the Project ID (which is in the request). Is there a way to do this with formidable or perhaps multer?
// Upload Image
router.post("/project_image", function(req, res, next) {
const form = new IncomingForm({
uploadDir: process.cwd() + "/public/images", // <- e.g. I would like this to be `/public/images/${req.body.project_id}`
keepExtensions: true
});
form.parse(req);
let project;
form.on("field", (name, value) => {
project = JSON.parse(value);
});
form.on("file", (field, file) => {
let path = file.path;
let fileName = path.substr(path.lastIndexOf("upload"));
return req.db
.from("projects")
.where("id", "=", project.project_id)
.update({ image: "/images/" + fileName })
.then(() => {
return res.status(200).json({
message: "Image Upload Successful",
error: false
});
})
form.on("end", () => {});
});
Thank you.
Solved it myself with the following. Basically I move the file to its intended destination.
// Upload Image
router.post("/project_image", function(req, res, next) {
const directory = process.cwd() + "/public/images";
const form = new IncomingForm({
uploadDir: directory,
keepExtensions: true
});
form.parse(req);
let project;
form.on("field", (name, value) => {
project = JSON.parse(value);
});
form.on("file", (field, file) => {
let path = file.path;
let fileName = path.substr(path.lastIndexOf("upload"));
let destinationPath = directory + `/${project.project_id}/`;
if (fs.existsSync(destinationPath)) {
moveFile(path, destinationPath);
} else {
fs.mkdirSync(directory + `/${project.project_id}/`);
moveFile(path, destinationPath);
}
return req.db
.from("projects")
.where("id", "=", project.project_id)
.update({ image: "/images/" + fileName })
.then(() => {
return res.status(200).json({
message: "Image Upload Successful",
error: false
});
})
form.on("end", () => {});
});
};

Firebase cloud function storage trigger first thumbnail urls are fine then the next ones are all the same thumbnails urls as the first

I am trying to upload an image to firebase and then produce 2 thumbnails. I am able to do this with no problems. My current road block is when I write the urls to the realtime database, I am always getting the same url as the initial upload.
For example:
1st upload I get my uploaded image with the two proper thumbnails for the image
2nd upload I get my uploaded image with the two previous thumbnails (first image)
3rd upload I get my uploaded image with the first images thumbnails...
...this continues to reproduce the urls for the first upload
In my storage the correct thumbnails are being generated, but the urls are always for the first upload?
I don't know if this is a problem with the getSignedUrl() or not, really not sure whats going on here.
Here is my cloud function:
export const generateThumbs = functions.storage
.object()
.onFinalize(async object => {
const bucket = gcs.bucket(object.bucket); // The Storage object.
// console.log(object);
console.log(object.name);
const filePath = object.name; // File path in the bucket.
const fileName = filePath.split('/').pop();
const bucketDir = dirname(filePath);
const workingDir = join(tmpdir(), 'thumbs');
const tmpFilePath = join(workingDir, 'source.png');
if (fileName.includes('thumb#') || !object.contentType.includes('image')) {
console.log('exiting function');
return false;
}
// 1. ensure thumbnail dir exists
await fs.ensureDir(workingDir);
// 2. Download Sounrce fileName
await bucket.file(filePath).download({
destination: tmpFilePath
});
//3. resize the images and define an array of upload promises
const sizes = [64, 256];
const uploadPromises = sizes.map(async size => {
const thumbName = `thumb#${size}_${fileName}`;
const thumbPath = join(workingDir, thumbName);
//Resize source image
await sharp(tmpFilePath)
.resize(size, size)
.toFile(thumbPath);
//upload to gcs
return bucket.upload(thumbPath, {
destination: join(bucketDir, thumbName),
metadata: {
contentType: 'image/jpeg'
}
}).then((data) => {
const file = data[0]
// console.log(data)
file.getSignedUrl({
action: 'read',
expires: '03-17-2100'
}).then((response) => {
const url = response[0];
if (size === 64) {
// console.log('generated 64');
return admin.database().ref('profileThumbs').child(fileName).set({ thumb: url });
} else {
// console.log('generated 128');
return admin.database().ref('categories').child(fileName).child('thumb').set(url);
}
})
.catch(function (error) {
console.error(err);
return;
});
})
});
//4. Run the upload operations
await Promise.all(uploadPromises);
//5. Cleanup remove the tmp/thumbs from the filesystem
return fs.remove(workingDir);
})
Cleaned up my code and solved my problem, here is how I generated the urls and passed them to the proper URLs by accessing the users UID and postId in the file path:
export const generateThumbs = functions.storage
.object()
.onFinalize(async object => {
const fileBucket = object.bucket; // The Storage bucket that contains the file.
const filePath = object.name; // File path in the bucket.
const fileName = filePath.split('/').pop();
const userUid = filePath.split('/')[2];
const sizes = [64, 256];
const bucketDir = dirname(filePath);
console.log(userUid);
if (fileName.includes('thumb#') || !object.contentType.includes('image')) {
console.log('exiting function');
return false;
}
const bucket = gcs.bucket(fileBucket);
const tempFilePath = path.join(tmpdir(), fileName);
return bucket.file(filePath).download({
destination: tempFilePath
}).then(() => {
sizes.map(size => {
const newFileName = `thumb#${size}_${fileName}.png`
const newFileTemp = path.join(tmpdir(), newFileName);
const newFilePath = `thumbs/${newFileName}`
return sharp(tempFilePath)
.resize(size, size)
.toFile(newFileTemp, () => {
return bucket.upload(newFileTemp, {
destination: join(bucketDir, newFilePath),
metadata: {
contentType: 'image/jpeg'
}
}).then((data) => {
const file = data[0]
console.log(data)
file.getSignedUrl({
action: 'read',
expires: '03-17-2100'
}, function(err, url) {
console.log(url);
if (err) {
console.error(err);
return;
}
if (size === 64) {
return admin.database().ref('profileThumbs').child(userUid).child(fileName).set({ thumb: url });
} else {
return admin.database().ref('categories').child(fileName).child('thumb').set(url);
}
})
})
})
})
}).catch(error =>{
console.log(error);
});
})

Categories