nodejs multer retrieve stored images to the client side - node.js

I have created file storage in nodejs application to store images in uploads folder. which works fine but now I want to display those images I have read some other posts but I am still quite unsure.
Here is my code:
var Storage = multer.diskStorage({
destination: function(req, file, callback) {
var pathname = file.originalname;
var path = pathname[0].replace('_','/');
cb(null,'./uploads'+pathname);
},
filename: function(req, file, callback) {
var pathname = file.originalname;
var filename = pathname[1];
if(pathname!=undefined)
callback(null, pathname);
}
});
var upload = multer({ storage: Storage }).single('file');
router.post('/upload', multer({dest:'./uploads/'}).single('file'), function(req,res)
{
return res.status(201).json({result:req.file});
});
from console log I get
{ fieldname: 'file',
originalname: '1569402978357.jpg',
encoding: '7bit',
mimetype: 'image/jpeg',
destination: './uploads/',
filename: 'ada9282345565e8a77c6adc4b2d15836',
path: 'uploads\\ada9282345565e8a77c6adc4b2d15836',
size: 170272 }
and in the uploads it is stored as filename
my problem is how can I display this image back? should call it from stored filename or should I change the storage to save it as original filename with file extension?

The choice to keep the original file name differs from use case to use case. In either case you will have to store the name somewhere in a persistence medium (like a database) so that while displaying it back you can look into your uploads directory and send the file back.

Depending on your use case, if you want to store the files in a different name, you need a way to find the files at a later stage. It can be achieved by storing the names in a database or you could even use some kind of formula to find out the file name of the stored file from the original one.
E.g:
name_of_stored_file = md5(name_of_original_file)
As files (images) are now stored in your backend, you can expose some API to send the files to client on request. Again this completely depends on the use case, but here's a simple example of using Expressjs res.sendFile method:
app.get('/file/:fileName', function (req, res) {
const filePath = // find out the filePath based on given fileName
res.sendFile(filePath);
});
http://expressjs.com/en/api.html#res.sendFile

Related

Heroku images are gone in new pushes

Need your Help!
I deployed my nodejs app (only backend) to heroku and it includes a part where the user must insert documents in format jpg, jpeg, png! All pictures are located in the static folder (/assets/docs)! before pushing the changed code, I tried to create users and their pictures are shown! but when I do a new push to heroku, old files are gone and not showing!
I tried to ignore that static folder in new pushes - /assets/docs/* useless, /assets - useless, /assets/docs - useless
But as an experiment, I put 1 .jpg file in that static folder and pushed it. it always stays even in new pushes (with ignores also)
Using multer and mongoose
file uploading with multer
const multer = require('multer')
const path = require('path')
const storage = multer.diskStorage({
destination: 'assets/docs/',
filename: function(req, file, cb) {
cb(null, file.fieldname + '-' + Date.now() + path.extname(file.originalname))
}
})
const up = multer({
storage: storage,
limits: {
fileSize: 5 * 1024 * 1024
},
fileFilter(req, file, cb) {
if (!file.originalname.match(/\.(png|jpg|jpeg|docx|doc|pdf)$/)) {
return cb (Error("Allowed file types are png jpg jpeg docx doc pdf"), false)
}
cb (null, true)
}
})
saving files - mongoose
router.post('/register-employee', uploadDocument.single('passport'), async (req, res) => {
try {
const emp = await new Employee({
...req.body,
passport: `docs/${req.file.filename}`
})
await emp.save()
res.json(emp)
} catch (e) {
res.status(500).json({errorMessage: e.message})
}
})
gitignore file
/node_modules
/.vscode
/.idea
/config
/assets/docs
setting up a public folder
const publicDir = path.join(__dirname, '../assets')
app.use(express.static(publicDir))
The Heroku filesystem is ephemeral - that means that any changes to the filesystem whilst the dyno is running only last until that dyno is shut down or restarted.
Each dyno boots with a clean copy of the filesystem from the most recent deploy. This is similar to how many container based systems, such as Docker, operate.
You can get over this issue by using an external file storage system like Amazon s3 to persist data across restarts on a dyno.
You can read more here: https://help.heroku.com/K1PPS2WM/why-are-my-file-uploads-missing-deleted

Firebase Cloud Function Serving Local File for Download

I created cloud function that generates an xlsx file, I need the user to download that file after it's generated.
Method 1: Upload to Bucket, then redirect
So far i've tried uploading the file to a bucket using this API, and then redirect him to the bucket file url, I also double checked the bucket name using this API, but I get the same error every time:
{"error":{"code":500,"status":"INTERNAL","message":"function crashed","errors":["socket hang up"]}}
Portion of the code that contains uploading to bucket:
const { Storage } = require('#google-cloud/storage');
const storage = new Storage();
await storage.bucket('bucket-name').upload('myfile.xlsx', {
gzip: false,
});
Portion of the code that proves file exists:
fs.access('myfile.xlsx', fs.constants.F_OK, (err) => {
console.log(`${file} ${err ? 'does not exist' : 'exists'}`);
});
I also checked if the library "#google-cloud/storage" reads the file, and it reads it correctly and gets the file size right.
Method 2: Direct Download
Download the file directly, the problem is that every doc online for nodejs to download a local file to the user is setting up a custom server to download the file, but i'm using firebase, so it's not in control of that server.
Just wanted to add more detail to the answer, since there's no need to write into a file and read from it to download it's data, simply take the data and send it, using the few lines below.
res.setHeader('Content-Type', 'application/vnd.openxmlformats');
res.setHeader("Content-Disposition", "attachment; filename=" + fileName);
res.end(fileData, 'binary');
If your excel file is created and should be returned to the client as a response to an HTTP request (calling to an API endpoint) then this is how you can do it.
export const getExcelFile = functions.https.onRequest(async (request, response) => {
// ...
// Create your file and such
// ..
await storage.bucket('bucket-name').upload('myfile.xlsx', {
gzip: false,
});
response.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
response.send(fs.readFileSync('myfile.xlsx'));
return null;
});
Otherwise, if the excel file is created as a response to an event, and you want the user to download the file at another time, then you create a download link and serve it to the user in any way you want.
// ...
// Create your file and such
// ..
const [file] = await storage.bucket('bucket-name').upload('myfile.xlsx', {
gzip: false,
});
const [downloadUrl] = await file.getSignedUrl({
action: 'read',
expires: '20-03-2019' // Link expiry date: DD-MM-YYYY
});
console.log(downloadUrl);

image upload from one server to other in nodejs [duplicate]

This question already has answers here:
Nodejs POST request multipart/form-data
(5 answers)
Closed 5 years ago.
File uploading using multer is not happening
My code:
File read from html and pass to external url
router.post('/fileUpload',function (req,res) {
request({
uri: http//example.com/upload, // url of other server
method: "POST",
form: {
"name":req.body.name,
"image":? //image from html - no idea how to pass the req.files here
}
}, function (error, response, body) {
------------
------------
}
});
other server url : /example.com/upload
This is the code to upload image using multer
app.post('/upload',function(req,res){
var storage = multer.diskStorage({
destination: function (req, file, callback) {
callback(null, 'uploads');
},
filename: function (req, file, callback) {
callback(null, file.fieldname + '-' + Date.now());
}
});
var upload = multer({ storage : storage }).array('productImage');
upload(req,res,function(err) {
if(err) {
return res.json({'success':0,'result':{},'errorMessage':'Unknown Error'});
}
return res.json({'success':1,'result':{},'errorMessage':''});
});
});
Create readStream from file uploaded and pipe it to the another server.check this link https://www.npmjs.com/package/request, you will easily get this done.
The simple answer would be to create a read stream from the uploaded file and pipe it to the second server, like so:
fs.createReadStream(req.files[0].path).pipe(request.post('http//example.com/upload'))
However, there are many ways to make this work. The most efficient of which is to use a binary stream from the initial upload all the way to the second server. You want to avoid using your first server as a storage for all of the uploads.
Here's how I would do it instead:
Use jQuery file upload on client side upload
(Or any other library/approach to upload the raw binary stream)
$('#fileupload').fileupload({
url: 'https://server1.com/upload'
})
Use Formidable (or other library like multer) to handle upload server-side
(This will allow you to read the binary stream in parts, and handle each part as it comes)
app.post('/upload',function(req,res){
var form = new formidable.IncomingForm();
form.parse(req);
form.onPart = function(part) {
fs.createReadStream(part).pipe( request.post('http//example.com/upload'))
}
}
When each part of the binary upload is received, you can to stream the binary directly to the second server via pipe() to avoid having to store it on the first server whatsoever.
The key to making this work is to look at the file upload and transfer as a stream of binary bits. When the user uploads a file (req.body) you want to create a read stream from the file, and pipe the binary over the request.post().

Images are not storing in Database using multer

I am trying to store image in local folder and save image name in mongodb using multer with the below code.
var uploadDir=__dirname+ '/assets/images';
var images='.jpg';
var storage=multer.diskStorage({
destination:function(req, file, cb){
cb(null, uploadDir);
},
filename:function(req, file, cb){
console.log(file);
image=Date.now()+images;
callback(null, images);
}
});
var upload=multer({storage:storage}).single('img');
But its not storing image in local folder and only its saving .jpg string in image field in mongodb. I think the problem is that filename: method is not working. Please review it and help me to find the solution.
Thanks,
Your error is in the multer filename configuration. You were using the images variable in there and that's why wasn't storing the image in the right path and only with the .jpg name. It is a good idea to define the var image as var, otherwise will be defined in the global namespace with side effects that you can not predict. This should work:
var uploadDir=__dirname+ '/assets/images';
var images='.jpg';
var storage=multer.diskStorage({
destination:function(req, file, cb){
cb(null, uploadDir);
},
filename:function(req, file, cb){
console.log(file);
var image=Date.now()+images;
callback(null, image);
}
});

Referencing multiple files upload to DB with node.js

I can't understand how to save references (filenames) of multiple files upload to the database using Node.Js.
I'm using blueimp jquery file upload and multer middleware to perform a multiple files upload to the local storage (uploads folder).
<input id="fileupload" type="file" name="images" data-url="/record/edit/#{record.id}" multiple>
app.js code:
//using multer to upload files
const upload_images = upload.fields([{ name: 'featured_img', maxCount: 1 }, { name: 'images', maxCount: 8 }]);
//...
app.post('/record/edit/:id', upload_images, recordController.postUpdate );
recordController.js code:
exports.postUpdate = ((req, res, next) => {
Record.findById(req.params.id, (err, record) => {
if (req.files ) {
console.log('UPLOADED')
// record.featured_img = req.files['featured_img'][0].filename; //=> working for single file
// Now, how to make it work for multiple files?
console.log(req.files['images'])
record.images = "HOW TO SAVE THE INFO OF REQ.FILES TO DB?";
}
console.log output:
UPLOADED
[ { fieldname: 'images',
originalname: 'slippry-02.jpg',
encoding: '7bit',
mimetype: 'image/jpeg',
destination: 'C:\\Users\\agaez\\Documents\\sanbox\\nodeApps\\theapp\\uploads',
filename: '8mdnuacm1469201049450.jpg',
path: 'C:\\Users\\agaez\\Documents\\sanbox\\nodeApps\\theapp\\uploads\\8mdnuacm1469201049450.jpg',
size: 49798 } ]
POST /record/edit/578580b43c7a08601730cc06 302 26.654 ms - 82
UPLOADED
[ { fieldname: 'images',
originalname: 'slippry-03.jpg',
encoding: '7bit',
mimetype: 'image/jpeg',
destination: 'C:\\Users\\agaez\\Documents\\sanbox\\nodeApps\\theapp\\uploads',
filename: 'yrnnzl9h1469201049467.jpg',
path: 'C:\\Users\\agaez\\Documents\\sanbox\\nodeApps\\theapp\\uploads\\yrnnzl9h1469201049467.jpg',
size: 49792 } ]
Update
I'm using MongoDB as database and mongoose as ODM
More info: Curretly, I'm using jquery file upload which is making 2 post request when I upload 2 images. Without jquery file uploads it makes 1 post req for all the images uploaded and I get the req.files a unique array (with all the files uploaded). How can I save that info to MongoDB?
recordModel.js
//...
images: String,
//...
Your 'images' property looks just like a single String value, so if you are looking for just a comma separated string of URLs the easiest thing would be to just bring in Lodash and create the string like so:
record.images = _.map(req.files['images'], 'path').join(',');
That said, I think in reality you don't want just a single string, but instead you'd do well (I think) to define an ImageSchema that includes some or all of the information you're getting from req.files. At minimum, I'd pull in the path and the mimeType, but other info could be useful to you, I don't know. In any case, given that you have an ImageSchema defined, you could do:
const Image = mongoose.model('Image');
/* your upload stuff happens, and then */
record.images = _.map(req.files['images'], function(i){ return new Image(i);});
That assumes of course that you update your RecordSchema to be:
...
images : [ImageSchema]
...
Hope that helps.

Resources