Nodejs upload multiple images - create dir when doesnt exist - javascript

Im using nodejs and multer to upload multiple images.
Firsat check is if dir exist. If not will be created.
Error: When folder doesnt exist all images are passing the first condition fs.access by giving the message "Directory doesnt exist" but then dir is created so the second image gets an error "Directory exist".
var storage = multer.diskStorage({
destination: (req, file, cb) => {
const userId = encryptFolder(req.params["id"]);
const dtrToCreate = "C:/Users/User/Pictures/" + userId;
fs.access(dtrToCreate, (error) => {
if (error) {
console.log("Directory does not exist.", userId);
fs.mkdirSync(dtrToCreate, (error, data) => {
if (error) {
throw error;
}
cb(null, "C:/Users/User/Pictures/");
});
} else {
console.log("Directory exists.", userId);
cb(null, "C:/Users/User/Pictures/");
}
});
},
When directory exist images are uploaded sucessfully.

Working solution:
Since there are multiple files should be a recusrsive function to check if folder exist each time files passing.
fs.mkdirSync(dtrToCreate, { recursive: true })
return cb(null, dtrToCreate)

Related

Azure storage avoid uploading file if name already exists

I am trying to upload a file to azure storage. I have it working using the package multer-azure however if I upload a file with the same name of one already stored in the storage, the first one will get replaced.
From docs it seems like I need to add a ETagMatch but I am not sure where this should go.
https://azure.github.io/azure-storage-node/global.html#AccessConditions
My code:
var upload = multer({
storage: multerAzure({
account: STORAGE_ACCOUNT_NAME, //The name of the Azure storage account
key: ACCOUNT_ACCESS_KEY, //A key listed under Access keys in the storage account pane
container: 'demo', //Any cntainer name, it will be created if it doesn't exist
blobPathResolver: (_req, file, callback) => {
let path;
if(_req.body.pathToFile) {
path = `${organization}/${_req.body.pathToFile}/${file.originalname}`
} else {
path = `${organization}/${file.originalname}`;
}
// var blobPath = yourMagicLogic(req, file); //Calculate blobPath in your own way.
callback(null, path);
}
})
}).single('file')
upload(req, res, function (err) {
if (err instanceof multer.MulterError) {
return res.status(500).json(err)
} else if (err) {
return res.status(500).json(err)
}
return res.status(200).send(req.file)
})
If the blob already exists on the service, it will be overwritten. You can simply use doesBlobExist method of the SDK to check if a blob exists in a path in NodeJS. If not exist, you could upload a file to Azure storage.
var blobService = azure.createBlobService(storageAccount, accessKey);
blobService.doesBlobExist('azureblob', 'xxx/a.json', function(error, result) {
if (!error) {
if (result.exists) {
console.log('Blob exists...');
} else {
console.log('Blob does not exist...');
}
}
});

Read Stream not doing firing / catching errors

I am trying to create a read stream to use Cloudinary's upload stream function, I am also using resumable.js to chunk the initial file, while the create read stream is working perfectly fine (as the whole file gets written perfectly fine.) the read stream / cloudinary upload function seems to not even be firing and failing silently.
router.post("/upload", (req, res, next) => {
console.log("the params are.. ", req.body);
resumable.post(req, function(
status,
filename,
original_filename,
identifier
) {
if (status === "done") {
let timestamp = new Date().getTime().toString();
//stich the chunks
var s = fs.createWriteStream(timestamp + filename);
resumable.write(identifier, s);
var upload_stream = cloudinary.uploader.upload_stream(
{ tags: "basic_sample" },
function(err, image) {
console.log();
console.log("** Stream Upload");
if (err) {
console.warn(err);
}
console.log("* Same image, uploaded via stream");
console.log("* " + image.public_id);
console.log("* " + image.url);
waitForAllUploads(timestamp + filename, err, image);
}
);
fs.createReadStream(timestamp + filename)
.pipe(upload_stream)
.on("error", err => {
console.log(err);
});
s.on("finish", function() {
// Stream upload
console.log("ive finished...");
// delete chunks
setTimeout(() => {
resumable.clean(identifier);
}, 1000);
});
}
res.send(status);
});
});
Here are the resources to what I am using:
https://github.com/cloudinary/cloudinary_npm/blob/master/samples/basic/basic.js
https://github.com/mrawdon/resumable-node
fs.createReadStream(timestamp + filename) accepts the file path but looks like you are passing the timestamp as well. Also, waitForAllUploads is not defined. You can try the following code just using Node and Cloudinary to test it out.
var upload_stream= cloudinary.uploader.upload_stream({tags: 'basic_sample'},function(err,image) {
console.log("** Stream Upload");
if (err){ console.warn(err);}
console.log("* "+image.url)
});
var file_reader = fs.createReadStream('<file path>').pipe(upload_stream);

Missing required parameter - public_id, and what to assign to imageId for Cloudinary

I'm building a website that users can upload insights and comments on reading novels,
and users are free to fetch images of the novel or not.
If an image is chosen, the post schema has image & imageId attribute for Cloudinary uploads and future manipulation such as changing(update route) or deleting(destroy route) it from the Cloudinary library.
If no image is chosen, then a default image takes place.
Problem is, I managed to make default image function work, however I don't want multiple same default images uploaded to Cloudinary library, so I put default_image.jpg in local server folder (/public/images) to be exact), and this default_image.jpg shouldn't be in Cloudinary library and this should save a lot of capacity.
However, for these posts without a chosen image, I dunno what I should assign for their imageId property.
I tried undefined and null, and of course, they didn't work, cuz' this way it won't be able to find certain novel.imageId if they're all undefined or null.
// Schema for data
var fictionSchema = new mongoose.Schema({
...
...
image: String,
// for cloudinary API to track which image to delete or update
imageId: String,
...
...
});
// CREATE route
router.post("/", middleware.isLoggedIn, upload.single('image'), (req, res) => {
if (req.file){
// req.file.path comes from multer npm
cloudinary.v2.uploader.upload(req.file.path, function (err, result) {
if (err) {
...
}
// add cloudinary url for the image to the novel object under image property
req.body.novel.image = result.secure_url;
req.body.novel.imageId = result.public_id;
});
} else {
// setup default image for new novels
req.body.novel.image = "/images/noimage.jpg";
// imageId here should be ... empty? Undefined? Null? A fixed ID but may be delete when accessing Destroy route?
req.body.novel.imageId = undefined;
}
...
...
Novel.create(req.body.novel, function (err, newlyAddedNovel) {
if (err) {
req.flash('error', err.message);
...
} else {
req.flash("success", "Novel added successfully.");
...
}
});
});
// DESTROY route
router.delete("/:id", middleware.checkNovelOwnership, (req, res) => {
// find and delete the correct novel along with the image on cloudinary
Novel.findById(req.params.id, async (err, novel) => {
if (err) {
req.flash("error", err.message);
return res.redirect("back");
}
try {
await cloudinary.v2.uploader.destroy(novel.imageId);
// delete the novel found
novel.remove();
// delete the image from cloudinary
req.flash("success", "Novel deleted successfully.");
...
...
} catch (err) {
..
}
});
});
Good news, I solved the problem! Her-ray! Took me around 2 hrs though...
So, in the end everything worked just as I wanted, such finesse.
Codes are as below:
• CREATE route
For new posts, I set up every new novel with the default image first, then if there's an image (req.file) given, change it and upload.
Afterwards, remember to create that novel data in mongo database. (Novel.create() in my case.)
router.post("/", middleware.isLoggedIn, upload.single('image'), async function (req, res) {
// set every new novel with default image first
req.body.novel.image = "https://res.cloudinary.com/dvfkbz6la/image/upload/v1565434656/noimage_ew1uri.jpg";
req.body.novel.imageId = "noimage_ew1uri";
req.body.novel.user = {
id: req.user._id,
username: req.user.username
};
if (req.file) {
// req.file.path comes from multer npm
await cloudinary.v2.uploader.upload(req.file.path, function (err, result) {
if (err) {
req.flash('error', err.message);
return res.redirect('back');
}
// add cloudinary url for the image to the novel object under image property
// add image's public_id (imageId in novel model) to novel object
req.body.novel.image = result.secure_url;
req.body.novel.imageId = result.public_id;
});
}
Novel.create(req.body.novel, function (err, newlyAddedNovel) {
if (err) {
...
} else {
...
}
});
});
• UPDATE route
In try block, if (novel.imageId = "DEFAULT_IMAGEID") { } else { } is added.
// UPDATE route
router.put("/:id", middleware.checkNovelOwnership, upload.single('image'), (req, res) => {
// find the correct novel
Novel.findById(req.params.id, async (err, novel) => {
if (err) {
...
} else {
// if there's a req.file, we know user is trying to upload a new image
if (req.file) {
try {
// if imageId is default, await the result to be uploaded
if (novel.imageId = "noimage_ew1uri") {
var result = await cloudinary.v2.uploader.upload(req.file.path);
} else {
// if not default, find the old image using imageId and delete
await cloudinary.v2.uploader.destroy(novel.imageId);
var result = await cloudinary.v2.uploader.upload(req.file.path);
}
novel.imageId = result.public_id;
novel.image = result.secure_url;
} catch (err) {
...
}
}
novel.title = req.body.title;
novel.author = req.body.author;
novel.price = req.body.price;
novel.description = req.body.description;
// remember to save the changed novel
novel.save();
req.flash("success", "Successfully Updated!");
res.redirect("/novels/" + novel._id);
}
});
});
• DESTROY route
In try block, if (novel.imageId != "DEFAULT_IMAGEID") { } else { } is added.
// DESTROY route
router.delete("/:id", middleware.checkNovelOwnership, (req, res) => {
// find and delete the correct novel along with the image on cloudinary
Novel.findById(req.params.id, async (err, novel) => {
if (err) {
req.flash("error", err.message);
return res.redirect("back");
}
try {
// if novel.imageId isn't default, destroy it from Cloudinary
if (novel.imageId != "noimage_ew1uri") {
await cloudinary.v2.uploader.destroy(novel.imageId);
}
// delete the novel found
novel.remove();
...
} catch (err) {
...
}
});
});
Only problem left is, I dunno why but when hitting UPDATE route,
and when the imageId isn't the default one and the user changed the image,
the old image will NOT be destroyed and stayed in Cloudinary library. Such oddness.
I sure have this code set up
Novel.findById(req.params.id, async (err, novel) => {
if (err) {
...
} else {
if (req.file) {
try {
// if imageId is default, await the result to be uploaded
if (novel.imageId = "noimage_ew1uri") {
...
} else {
// if not default, find the old image using imageId and delete
await cloudinary.v2.uploader.destroy(novel.imageId);
var result = await cloudinary.v2.uploader.upload(req.file.path);
}
...
...
Dunno why await cloudinary.v2.uploader.destroy(novel.imageId); isn't working as expected. Hmmm...
Images that were already available in your site, are cached in one or more CDN edges. Therefore, even if you delete the image from your Cloudinary account, cached copies might still be available. In default, all delivered images are cached for 30 days.
When using the destroy API with the invalidate parameter set to true, the image will be deleted and purged from the CDN, allowing for the new image to be displayed.
Alternatively, when updating, you can accomplish the same result by setting the overwrite and invalidate parameters to true when uploading the new file (without the need to call the destroy method).

Error: ENOENT: no such file or directory

I am trying to get get the folder path from user's selection and for each file I read it and return the data. However upon getting the file I cannot read the data for some reason that I have't been able to understand yet. The directory I am trying to read and render do exist. I have referred to the other similar posts as well.
readFolder() {
dialog.showOpenDialog({ properties: ['openDirectory'] }, (dirFiles) => {
console.log(dirFiles);
if (dirFiles === undefined) {
console.log('No file ');
return;
}
const pathName = dirFiles[0];
fs.readdir(pathName, (err, files) => {
files.forEach(file => {
fs.readFile(file, 'utf-8', (err, data) => {
if (err) {
console.log(`something went wrong ${err}`);
} else {
console.log(data);
}
});
});
});
});
}
readdir returns filenames "a", "b", "c" etc. You want pathName + '/' + file for "/path/to/file/a"
The mistake I made was the fact that I hadn't realised the return values of 'file' which are just the names of the files as strings but not paths. Assinging let filePath =${pathName}/${file}; and reading it onwards solved the problem.

Electron Dialog not saving the file

Electron version: 1.3.3
Operating system: Ubuntu 14.04
I want to save a XML object into a .xml file with Electron. I try this:
const {dialog} = require("electron").remote;
dialog.showSaveDialog(myObj)
A new windows is opening, I fill the name of the file but nothing has been saving.
it's recommended to use returned path from dialog.showSaveDialog to get filepath in new versions of electron: (which is result.filePath in the below code)
filename = dialog.showSaveDialog({}
).then(result => {
filename = result.filePath;
if (filename === undefined) {
alert('the user clicked the btn but didn\'t created a file');
return;
}
fs.writeFile(filename, content, (err) => {
if (err) {
alert('an error ocurred with file creation ' + err.message);
return
}
alert('WE CREATED YOUR FILE SUCCESFULLY');
})
alert('we End');
}).catch(err => {
alert(err)
})
The showSaveDialog() API does not save the file for you. You must use the returned path and use Node to save your file.
const {dialog} = require('electron').remote;
const fs = require('fs');
dialog.showSaveDialog({}).then((result) => {
fs.writeFile(result.filePath, MyFileData, (err) => {
// file saved or err
});
}).catch((err) => {
// err
});

Categories

Resources