Not getting the success response after video upload on s3 node.js - node.js

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.

Related

Express, Multer, and Cloudinary not returning full cloudinary response

I'm using Multer/multer-storage-cloudinary to upload images directly to Cloudinary rather first uploading it to a local temp directory, then sending it to Cloudinary:
const express = require('express');
const router = express.Router({mergeParams:true});
if (app.get('env') == 'development'){ require('dotenv').config(); }
const crypto = require('crypto');
const cloudinary = require('cloudinary').v2;
const { CloudinaryStorage } = require('multer-storage-cloudinary');
const multer = require('multer');
const { storage } = require('../cloudinary');
const upload = multer({storage});
//configure cloudinary upload settings
cloudinary.config({
cloud_name:process.env.CLOUDINARY_NAME,
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET
});
const storage = new CloudinaryStorage({
cloudinary: cloudinary,
folder: ('book_tracker/'+process.env.CLOUDINARY_FOLDER+'posts'),
allowedFormats: ['jpeg', 'jpg', 'png'],
filename: function (req, file, cb) {
let buf = crypto.randomBytes(16);
buf = buf.toString('hex');
let uniqFileName = file.originalname.replace(/\.jpeg|\.jpg|\.png/ig, '');
uniqFileName += buf;
console.log(req.body);
cb(undefined, uniqFileName );
}
});
const middleware = {
function asyncErrorHandler: (fn) =>
(req, res, next) => {
Promise.resolve(fn(req, res, next))
.catch(next);
}
}
/* POST create user page */
router.post('/register', upload.single('image'), asyncErrorHandler(postRegister));
What I'm running into is that the response I'm getting in req.file is not the full Cloudinary response which includes public_id, etc. Instead it's like this:
{
fieldname: 'image',
originalname: 'My Headshot.jpg',
encoding: '7bit',
mimetype: 'image/jpeg',
path: 'https://res.cloudinary.com/<cloudinary_name>/image/upload/v1611267647/<public_id>.jpg',
size: 379632,
filename: '<public_id>'
}
It's been a while since I worked with multer-storage-cloudinary, though that storage was taken directly from an old project that would return the correct information. Is there something in multer, or multer-storage-cloudinary, that I need to set in order to put the full cloudinary response into req.file?
The multer-storage-cloudinary package is a third party package that integrates multer and Cloudinary in a streamlined way, but it doesn't expose all possible options or responses from the Cloudinary SDK or API
In your example, it's not returning the full API response from Cloudinary, but a subset of the fields, because the file object's filename, path, and size properties are taken from the Cloudinary API response (from the public_id, secure_url, and bytes properties of the API response respectively), but the other fields aren't mapped: https://github.com/affanshahid/multer-storage-cloudinary#file-properties
If you need the full set of response values (or some specific values not mapped already) you can:
Ask the package maintainer to add support for other fields
Fork the package and map additional fields yourself; the fields are mapped here though I'm not sure what else may need to be changed: https://github.com/affanshahid/multer-storage-cloudinary/blob/1eb903d44ac6dd42eb1ab655b1e108acd97ed4ca/src/index.ts#L83-L86
Switch from using that package for wrapping the Cloudinary SDK to use the Cloudinary SDK directly in your own code, so you can handle the response directly.
Leave it as it is now and make a separate call to the Cloudinary Admin API to fetch the other details of the image(s): https://cloudinary.com/documentation/admin_api#get_resources
Leave it as-is, but add a notification_url so that as well as the API call response, the details of the new upload will be sent in an HTTP POST request to a URL of your choice: https://cloudinary.com/documentation/notifications
The notification_url can be specified in the Upload API call, Upload Preset, or at the account-level in the Cloudinary account settings.

Unable to upload file image using Ajax and Multer

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.

How to upload images to GCS bucket with multer and NodeJS?

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.

How to access files on digital ocean spaces with multer

I have managed to upload pdf files on digital ocean spaces with a node js app as shown below.
I don't know how to then access those files and display them to the user. I got the code below from this tutorial, object storage file upload, but there isn't an example on how to then access the files.
When I just try to access them with their url, I just get white space.
I have made them public but still get absolutely nothing trying to access them from the url.
Is there a way to access the files using multer still, do I have to make a get request with the RESTFul API? How do I access files stored in a digital ocean spaces?
This is how I upload files
const aws = require("aws-sdk");
const multer = require("multer");
const multerS3 = require("multer-s3");
const spaceEndPoint = new aws.Endpoint("ams3.digitaloceanspaces.com");
const s3 = new aws.S3({
endpoint:spaceEndPoint
})
const upload = multer({
storage:multerS3({
s3:s3,
bucket: "fileRepo",
acl:"public-read",
key:function(request, file, cb){
console.log(file);
cb(null, file.originalname);
}
})
}).array("upload",1);
router.get("uploadFile", function(req,res){
upload(req, res, function(error){
if(error){
console.log(error);
return res.redirect("/");
}
});
})
And this is how I try to retrive the pdfs
router.get("/contentPage", function(req, res){
var fileName = req.body.department;
var directory = "https://fileRepo.ams3.digitaloceanspaces.com/" + fileName + ".pdf";
res.render("fileview", {dir: directory});
})
<div id="departmentListWrapper" class="container">
<embed src="<%= dir %>" width="800px" height="2100px" />
</div>
Can anyone tell me what the issue might be with retrieving these files?
Try this code snippet, works fine for me
import multer from 'multer';
import multerS3 from 'multer-s3';
aws.config.update({
accessKeyId: 'your access key',
secretAccessKey: 'your secret key'
});
var filepath = "path of files folder"
// Create an S3 client setting the Endpoint to DigitalOcean Spaces
var spacesEndpoint = new aws.Endpoint('nyc3.digitaloceanspaces.com');
var s3 = new aws.S3({endpoint: spacesEndpoint});
var params = {
Bucket: bucketName,
Key: keyName,
Body: fs.createReadStream(filepath),
ACL: 'public-read'
};
s3.putObject(params, async function(err, data) {
if (err) {
console.log(err);
} else {
console.log(data);
}
});
It turns out that digital ocean did not know what type of file was being uploaded, I had to make it so that the file type is read before it is uploaded so that the browser knew what type of file I was trying to upload by adding the line
contentType: multerS3.AUTO_CONTENT_TYPE
to the multer object so that it looks like this.
const upload = multer({
storage:multerS3({
s3:s3,
bucket: "fileRepo",
contentType: multerS3.AUTO_CONTENT_TYPE
acl:"public-read",
key:function(request, file, cb){
console.log(file);
cb(null, file.originalname);
}
})
}).array("upload",1);

Combine multer and tinypng API in node

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.

Resources