6

Im able to upload an image to S3. Now, if the file selected is .gif, I want to be able to convert the .gif file to .mp4 and upload the converted file to S3. I am able to convert a .gif to .mp4 with ffmpeg only if I give the path of the file. How do I access the uploaded file from Multer? Below is my code :

var express = require('express');
var bodyParser = require('body-parser');
var app = express();
var aws = require('aws-sdk');
var multer = require('multer');
var multerS3 = require('multer-s3');
var s3 = new aws.S3();
var ffmpeg = require('fluent-ffmpeg');


var upload = multer({
    storage: multerS3({
        s3: s3,
        bucket: 'myBucket',
        key: function (req, file, cb) {
            console.log(file);
            var extension = file.originalname.substring(file.originalname.lastIndexOf('.')+1).toLowerCase();

                if(extension=="gif"){
                console.log("Uploaded a .gif file");

                ffmpeg(file) //THIS IS NOT WORKING
                    .setFfmpegPath("C:\\ffmpeg\\bin\\ffmpeg.exe")
                      .output('./outputs/2.mp4')    //TRYING TO UPLOAD LOCALLY, WHICH FAILS
                      .on('end', function() {
                        console.log('Finished processing');
                      })
                      .run();
            }

            cb(null, filename);
        }
    })
});

I'm trying to access the uploaded file like this: ffmpeg(file) since file is an argument passed in the multer function.

My form :

<form action="/upload" method="post"  enctype="multipart/form-data">
    <input type="file" name="file"> <br />
    <input type="submit" value="Upload">
</form>

In which part of the process do I convert the file?

Please help. Many thanks.

Somename
  • 2,836
  • 9
  • 34
  • 67

1 Answers1

8

You are trying to process a file locally that's on s3. The file needs to be your server's file system or at the very least be publicly available on s3. So you have two options here.

a) You could first upload all the files to the server where express is running (not on s3, first we store them temporarily). If the file is a .gif, process it and upload the resulting .mp4 file, otherwise upload to s3. Here's a working example:

var fs         = require('fs')
var path       = require('path')
var express    = require('express');
var bodyParser = require('body-parser');
var aws        = require('aws-sdk');
var multer     = require('multer');
var ffmpeg     = require('fluent-ffmpeg');
var shortid    = require('shortid');


aws.config.update(/* your config */);


var app    = express();
var s3     = new aws.S3();
var bucket = 'myBucket';

var upload = multer({
    storage: multer.diskStorage({
        destination: './uploads/',
        filename: function (req, file, cb){
            // user shortid.generate() alone if no extension is needed
            cb( null, shortid.generate() + path.parse(file.originalname).ext);
        }
    })
});


//----------------------------------------------------
app.post('/upload', upload.single('file'), function (req, res, next) {

    var fileInfo = path.parse(req.file.filename);

    if(fileInfo.ext === '.gif'){

        var videoPath = 'uploads/' + fileInfo.name + '.mp4';

        ffmpeg(req.file.path)
            .setFfmpegPath("C:\\ffmpeg\\bin\\ffmpeg.exe")
            .output(videoPath)
            .on('end', function() {
                console.log('[ffmpeg] processing done');
                uploadFile(videoPath, fileInfo.name + '.mp4');
            })
            .run();
    }
    else {
        uploadFile(req.file.path, req.file.filename);
    }

    res.end();
});


//----------------------------------------------------
function uploadFile(source, target){

    fs.readFile(source, function (err, data) {

        if (!err) {

            var params = {
                Bucket      : bucket,
                Key         : target,
                Body        : data
            };

            s3.putObject(params, function(err, data) {
                if (!err) {
                    console.log('[s3] file uploaded:');
                    console.log(data);
                    fs.unlink(source); // optionally delete the file
                }
                else {
                    console.log(err);
                }
            });
        }
    });
}


app.listen(3000);

b) Alternatively if you're ok with making your s3 files public you could upload them all using multer-s3. Since ffmpeg also accepts network locations as input paths you can pass it the s3 location of your .gif files and then upload the converted .mp4 files:

var fs         = require('fs')
var path       = require('path')
var express    = require('express');
var bodyParser = require('body-parser');
var aws        = require('aws-sdk');
var multer     = require('multer');
var ffmpeg     = require('fluent-ffmpeg');
var multerS3   = require('multer-s3');


aws.config.update(/* your config */);


var app    = express();
var s3     = new aws.S3();
var bucket = 'myBucket';

var upload = multer({
    storage: multerS3({
        s3: s3,
        bucket: bucket,
        key: function (req, file, cb) {
            cb(null, file.originalname);
        },
        acl: 'public-read'
    })
});

----------------------------------------------------
app.post('/upload', upload.single('file'), function (req, res, next) {

    var fileInfo = path.parse(req.file.originalname);

    if(fileInfo.ext === '.gif'){

        var videoPath = 'uploads/' + fileInfo.name + '.mp4';

        ffmpeg(req.file.location)
            .setFfmpegPath("C:\\ffmpeg\\bin\\ffmpeg.exe")
            .output(videoPath)
            .on('end', function() {
                console.log('[ffmpeg] processing done');
                uploadFile(videoPath, fileInfo.name + '.mp4');
            })
            .run();
    }

    res.end();
})


//----------------------------------------------------
function uploadFile(source, target){

    fs.readFile(source, 'base64', function (err, data) {

        if (!err) {

            var params = {
                Bucket      : bucket,
                Key         : target,
                Body        : data,
                ContentType : 'video/mp4'
            };

            s3.putObject(params, function(err, data) {
                if (!err) {
                    console.log('[s3] file uploaded:');
                    console.log(data);
                    fs.unlink(source); // optionally delete the file
                }
                else {
                    console.log(err);
                }
            });
        }
    });
}

app.listen(3000);

For both examples, remember to create an uploads/ folder and use your aws configuration.

cviejo
  • 4,201
  • 15
  • 27
  • Sorry was travelling. Tried the first example. The files uploaded to S3 are getting the `.mp4` extension regardless if they are `.gif` or not. The uploaded `.mp4` is not able to play, it says unknown file format. And this is for the `.gif` uploaded which saves in S3 as `.mp4`. – Somename Apr 13 '17 at 15:59
  • But the `.mp4` files that are in the `/uploads` folder are converted to `.mp4` and are playable. I commented the unlink part to check this. So it seems that the converted file is not getting uploaded to S3 and any file going to S3 is getting the `.mp4` extension. I like the first approach. Please help in making it work. – Somename Apr 13 '17 at 16:04
  • While you're at it mate, could you please tell me where do I rename the file? I am using `shortid` an npm package to generate URL friendly links. I'm guessing it to be in the `params` but not sure. Thanks a mil. – Somename Apr 13 '17 at 16:16
  • 1
    Edited option a), files keep their ending now. Also removed the `base64` encoding when reading, files on s3 are ok now. Using `shortid` instead of the original name. – cviejo Apr 13 '17 at 17:10
  • Uploading `.gif` is getting converted to `.mp4` and uploading fine to `S3`. Uploading `.jpg` is getting uploaded as .jpg but when I download it or try to open, its opening as `.mp4` also downloading as `.mp4`. Not able to figure out why. I believe because of `ContentType : 'video/mp4'` – Somename Apr 13 '17 at 17:24
  • I tried `ContentType : 'jpg, jpeg, video/mp4'` but non gifs are getting saved as `.mp4` – Somename Apr 13 '17 at 17:28
  • Downloaded the file that was saved as jog but was playing as mp4, renamed it to jog, and its opening fine. what could be the issue? – Somename Apr 13 '17 at 17:32
  • 1
    Was setting `ContentType : 'video/mp4'` for all uploads, removed that part. Should be ok now. – cviejo Apr 13 '17 at 17:37
  • You son of a gun you .. thanks a million !!! Really thank you .. Been trying this since more than 2 weeks. Thank you. God bless you! – Somename Apr 13 '17 at 17:47
  • One last thing, how do i access the name of the file from `app.post('/upload')`? In case I want to put the name in DB. I am talking about the new name generated by `shortid`. Thanks again. – Somename Apr 13 '17 at 17:50
  • 1
    Exactly. Awesome, glad to help! – cviejo Apr 13 '17 at 18:48
  • Please see my post [here](https://stackoverflow.com/questions/35541138/transform-upload-with-nodejs-multer/46026368#46026368) for another implementation of downloading the file from AWS S3 and resizing using sharp. – andrewhl Sep 03 '17 at 18:06
  • Please see [my comment](https://stackoverflow.com/questions/35541138/transform-upload-with-nodejs-multer/46026368#comment79013788_46026368) to your implementation. – cviejo Sep 03 '17 at 19:36