I'm facing issues for uploading local images to my google cloud storage.
I've already tried two methods. The first one is uploading with multer
var storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, './uploads/')
},
filename: (req, file, cb) => {
cb(null, file.fieldname + '-' + Date.now())
}
});
var upload = multer({storage: storage}).single('image');
app.post('/upload',function(req,res,next){
upload(req,res,(err) => {
if(err){
console.log(err)
}else{
console.log(req.file)
}
})
})
Then, i've tried directly with GCS
var bucket = admin.storage().bucket('mybucket')
app.post('/upload',function(req,res,next){
bucket
.save(file)
.then(() => {
})
for both of these solutions , req.files is always undefined whereas req.body is a buffer like this :
<Buffer 2d 2d 2d 2d ...>
when i try to save this buffer on my GCS bucket, i the .jpg/png file is created in my bucket but it is corrupted.
I'm browsing the web seeking for a solution but i found nothing that helped me to overcome this situation.
Any advice ?
You need multer, multer-google-storage and ofcourse bodyParser if you have additional form values. You need to sent data in multipart/form-data
In your .env file
GCS_BUCKET = <bucket name>
GCLOUD_PROJECT = <project id>
GCS_KEYFILE = <key file location>
You can download key file from GCP Console>Your Project>I AM & Admin>Service Accounts
In your route
const multer = require('multer');
const multerGoogleStorage = require("multer-google-storage");
var uploadHandler = multer({
storage: multerGoogleStorage.storageEngine()
});
router.post('/', uploadHandler.single('image'), function (req, res, next) {
const body = req.body;
res.json({fileName: req.file.filename});
res.end();
}
This will store file on to GCS with name [random-string-generated-by-gcs]_[YOUR FILE NAME WITH EXTENTION]. The same can be access under the route via req.file.filename.
Documentation
Make sure you have added enctype="multipart/form-data" attribute to your form. A probable reason for req.files being undefined.
Related
I am trying to make document management project using nodejs,mongodb and react. I want to make directory folder that will store document and the sub is it file. But I could not find any reference that I could use to start making the directory. Is there any specific term that I should search to get any reference to start the project? Thank you.
You can use Multer library for file uploads. It's super easy to use.
import multer from 'multer';
//specify where you want your files to be stored
var fileStorageEngine = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './images')
},
filename: function (req, file, cb) {
cb(null, Date.now() + '--' + file.originalname)
}
})
var upload = multer({ storage: fileStorageEngine });
//pass it as a middleware to a route where you expect to get a file upload
app.use('/api/file/', upload.single('image'), someRouterHere);
I am using the node.js as a backend and needs to upload the video on Amazon S3.
For that I am using the multer module but I need to know the efficient and standard way for uploading the video.
Generally we see that when we upload anything on the any good platfrom then there is a proper mechanism for uploading the video like:
When video is on uploading state, user get the response that how much percent is left for uploading the video
After uploading the video user get the response that video is uploaded successfully.
There is a handler which allow specific type of format to allow video.
There is a few limit size also which warn the user that maximum size is 20mb or 50 mb.
I am bit struggling about the good tutorial but unable to find as everywhere is sharing the tutorials about the image upload. So I thought I will raise the question which will help many others also regarding the same
I have implemented the process by which video will be uploaded on S3 but not getting the response after upload. It directly return the response and video will be uploaded in background.
I am sharing my implementation:
customapi.js file
const express = require('express');
const router = express.Router();
const helper = require('./file-upload');
const videoHandler = require('./videohandler');
// Post the video
router.post(
'/uploadvideo',
helper.single('media'),
videoHandler .uploadVideo
);
file-upload.js file
const AWS = require('aws-sdk')
const multer = require('multer')
const multerS3 = require('multer-s3')
const uuid = require('uuid/v1');
AWS.config.update({
accessKeyId: process.env.keyId,
secretAccessKey: process.env.accessKey,
});
const s3 = new AWS.S3();
const upload = multer({
storage: multerS3({
s3:s3,
bucket: process.env.bucketname,
acl: 'public-read',
contentType: multerS3.AUTO_CONTENT_TYPE,
metadata: function (req, file, cb) {
cb(null, {fieldName: file.fieldname})
},
key: function (req, file, cb) {
console.log(file) // This will print the filename which we can search in s3.
cb(null, uuid()+file.originalname)
console.log(uuid() + file.originalname)
}
})
})
videohandler.js file
module.exports = {
uploadVid: async (req, res) => {
try {
return res.send({message: "Done"})
} catch (error) {
console.log(error);
return res.send({message: "Error"})
}
}
}
I know what is happening exactly by which I am getting the response instantly instead of after the file upload.
When api calls -> helper.single('media') will invoke and process to start performing the task -> meanwhile videoHandler.uploadVideo will also called which directly send the response as there is no callback which understand the uploading process and return response accordingly.
Please share the best and efficient way as it supports many people also who is struggling for uploading the video.
Any help or suggestion is really appreciated.
Update Question
Many people sharing the way that video first upload on local disk and then it upload on S3. I need to know that is it a good behaviour. I mean for performing the task we need to do the double work instead of directly upload. It will consume the bandwidth and storage of the application on server.
I could handle it in this way. check whether is it applicable to your scenario.
App.js
router.post("/upload_service",
(req, res, next) => {
const upload = UploadController.upload.single('file')
upload(req, res, (err) => {
if (err) {
const error = new Error('Image upload error');
return next(error);
}
return next()
})
},
SomeController.createMethod)
UploadController.js
const multer = require('multer')
const multerS3 = require('multer-s3')
const AWS = require('aws-sdk')
const upload = multer({
storage: multerS3({
s3: new AWS.S3(),
bucket: 'bucket_name',
metadata: function (req, file, cb) {
cb(null, { fieldName: file.fieldname });
},
key: function (req, file, cb) {
const file_name_timestamp = Date.now().toString()
cb(null, `path/${file_name_timestamp}.${String(file.mimetype).split('/').pop()}`);
},
ContentType: "application/octet-stream",
})
})
module.exports = {
upload
}
SomeController.js
const createMethod = async function (req, res) {
if (!req.file) {
res.send('File missing')
}
}
Once file upload middleware was success, SomeController.createMethod will be triggered. using req.file, creation data can be visible.
While uploading a file from ajax request Multer is giving an error that is given below.
TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be one of
type string or Buffer. Received type object
at rite_ (_http_outgoing.js:595:11)
// code block for multer start
var Storage = multer.diskStorage({
destination: function(req, file, callback) {
callback(null, "./uploads/posts");
},
filename: function(req, file, callback) {
callback(null, file.fieldname + "_" + Date.now() + "_" + file.originalname);
}
});
var upload = multer({
storage: Storage
}).single('imgData');
//route Ajax Rquest URL Start
router.post('/blog/saveUploadImage',urlencoderParser,(req,res)=>{
upload(req, res, function(err) {
if (err) {
return res.end({UplaodStatus:true,type:'success',text:' š· Image Uploaded Now Saving Your Data It will take just a sec.'});
}
return res.end({UplaodStatus:false,type:'error',text:' ā¹ Sorry There was some Problem Uploading Image '});
});
});
//route Ajax Rquest URL End
//JS code
// code for geting file
let fileUpload = document.getElementById('uploadFile').files;
//appending the file to formdata
var formData = new FormData();
formData.append('imgData', fileUpload);
//AJAX Request
$.ajax({
enctype:'multipart/form-data',
data:formData,
url:'/admin/blog/saveUploadImage',
type:'POST',
cache:false,
contentType:false,
processData:false,
timeout:10000,
});
You're passing an array of files to formData.append(...), instead you should pick just the first element from this array:
let fileUpload = document.getElementById('uploadFile').files[0];
The issue was I Imported this package ( Look Below ) because of this multer was not working.
const fileUpload = require('express-fileupload');
So I removed it now it works fine.
Thank you for ur help.
The issue was I Imported this package ( Look Below ) because of this multer was not working.
const fileUpload = require('express-fileupload');
So I removed it now it works fine.
I'm fairly new to nodejs/ express, but no matter what I seem to do I cant seem to get multer to save to the specified destination, it seems to completely ignore the parameter all together. The code is shown below
//app.js
var multer = require('multer');
var fs = require('fs');
var apiRouter = express.Router();
var app = express();
var store = multer.diskStorage({
filename: function(req,file,cb){
console.log("filename");
cb(null, Date.now()+'.'+file.originalname);
},
desitnation: function(req,file,cb){
console.log("storage");
cb(null,'./public/');
}
});
var upload = multer({storage:store}).single('file');
apiRouter.post('/upload', function(req, res){
upload(req, res, function (err) {
if (err) {
return res.end(err.toString());
}
console.log(req.file);
return res.json({originalname:req.file.originalname, uploadname:req.file.filename});
});
});
The response I get when uploading is shown below:
GET /vendor.js.map 200 3.916 ms - 6636755
filename
{ fieldname: 'file',
originalname: 'Desert.jpg',
encoding: '7bit',
mimetype: 'image/jpeg',
destination: 'C:\\Users\\Dwyer\\AppData\\Local\\Temp',
filename: '1538979138829.Desert.jpg',
path:
'C:\\Users\\Dwyer\\AppData\\Local\\Temp\\1538979138829.Desert.jpg',
size: 845941 }
POST /api/upload 200 70.031 ms - 69
It seems to be setting the file correctly, but I'm not sure where it gets the destination from, no3 do I understand why the destination parameter isn't being read.
Its actually "destination" (not desitnation). You should also make sure that you have a folder ,with the specified name, at the specified destination.
Do you try using physical address in destination? I guess destination in multer document is physical address in linux os.
but no matter what I seem to do I cant seem to get multer to save to the specified destination, it seems to completely ignore the parameter all together.
This is because there seem to be a typo in the configuration.
'use strict';
var store = multer.diskStorage({
filename: function (req, file, cb) {
console.log("filename");
cb(null, Date.now() + '.' + file.originalname);
},
destination: function (req, file, cb) { // it is destination not desitnation :)
console.log("storage");
cb(null, './public/');
}
});
does anyone know how to use tinyPNG's API with multer? The docs seem deceptively simple:
var source = tinify.fromFile("unoptimized.jpg");
source.toFile("optimized.jpg");
though there's no clear indication of where this is meant to go, especially in something as convoluted as this:
var storage = multer.diskStorage(
{
destination: function (req, file, callback) {
callback(null, './uploads');
},
filename: function (req, file, callback) {
//use date to guarantee name uniqueness
callback(null, file.originalname + '-' + Date.now());
}
}
);
//.any() allows multiple file uploads
var upload = multer({ storage : storage}).any()
app.post('/api/photo', function(req,res){
upload(req,res,function(err) {
if(err) {
return res.end("Error uploading file.");
}
res.end("File is uploaded");
});
});
Where am I meant to "intercept" the file uploaded by multer so that I can compress it with tinyPNG?
Thanks in advance for the help!
Use following basic sample that changes uploaded photo/gallery files:
// Import express and multer.
var express = require('express');
var multer = require('multer');
// Setup upload.
var upload = multer({ dest: 'uploads/' });
var multipleFiles = upload.fields([{ name: 'photo', maxCount: 1 },
{ name: 'gallery', maxCount: 8 }]);
// Setup tinify.
var tinify = require("tinify");
tinify.key = "YOUR_API_KEY";
// Get request handler for '/' path.
var app = express();
app.get('/', function (req, res) {
res.setHeader("Content-Type", "text/html");
res.end(
"<form action='/api/photo' method='post' enctype='multipart/form-data'>" +
"<input type='file' name='photo' />" +
"<input type='file' name='gallery' multiple/>" +
"<input type='submit' />" +
"</form>"
);
});
// Upload file handler with '/api/photo' path.
app.post('/api/photo', multipleFiles, function (req, res) {
req.files['gallery'].forEach(function(file) {
// Your logic with tinify here.
var source = tinify.fromFile(file.path);
source.toFile(file.path + "_optimized.jpg");
});
res.end("UPLOAD COMPLETED!");
});
Feel free to change express middleware how you need it, just make sure you use upload.fields and authenticate using tinify.key = "YOUR_API_KEY";
https://github.com/expressjs/multer
https://tinypng.com/developers/reference/nodejs#compressing-images
I recently worked out a similar problem for myself using the tinify package and found the docs to be somewhat lacking.
I have a Vue front end collecting file uploads from the user using vue2dropzone. These are sent to a node / Express back end.
I have a need to compress the file and upload it to an S3 instance without storing on disk. That means using multer memory storage.
As a result there wonāt be an ability to use tinify.fromFile() as there is no file stored locally.
In my images middleware:
Const multer = require(āmulterā);
const tinify = require("tinify");
tinify.key = "your_key";
exports.singleFile = multer({ storage: multer.memoryStorage() }).fields([{ name: "file", maxCount: 1 }]);
exports.uploadCompImage = async (req, res, next) => {
try {
const fileName = `${req.params.name}${path.extname(req.files.file[0].originalname)}`;
const source = tinify.fromBuffer(req.files.file[0].buffer);
source.store({
service: "s3",
aws_access_key_id: "your_id",
aws_secret_access_key: "your_key
region: "your_region",
headers: {
"Cache-Control": "public"
},
path: `your_bucket/your_folder/${fileName}`
});
return res.status(200).send(`path_to_file/${fileName}`)
} catch (err) {
console.log(err);
next(err);
}
}
Then in my routes file:
Const images = require(ā../middleware/imagesā);
// skipped several lines for brevity
productRouter
.route("/images/:name")
.post(images.singleFile, images.uploadCompImage)
This process creates a multer singleFile upload to memoryStorage, making the file available at req.files.file[0] (req.files[āfileā] because I specified āfileā as the name in multer fields, loop through this array if uploading multiple).
After setting that up I get the file name, set the source by using tinify to read from req.files.file[0].buffer as a buffer.
Then I set the source to my s3 instance and send back a public link to the file.
Hopefully this answer helps you. I could definitely see altering the process to change where the file goes or even write it to disk by altering the multer options.