0

I want to create an API to receive uploaded file from web. And then I will process the checking on the file(to see whether a .png is a real .png instead of .exe), then send the file over to another API.

But now i am stuck at receiving file part.

I been searching around in google and stackoverflow, but neither of the method is work.

Where i host my API: Firebase Functions

Sample code:

html code:

<html>
  <body>
    <form ref='uploadForm' 
      id='uploadForm' 
      action='https://<project>.cloudfunctions.net/api/upload' 
      method='post' 
      encType="multipart/form-data">
        <input type="file" name="sampleFile" />
        <input type='submit' value='Upload!' />
    </form>     
  </body>
</html>

NodeJS Code:

formidable

const functions = require('firebase-functions');
const express = require('express');
const app = express();

app.post('/upload', function (req, res) {
    console.log('initializing upload')
    var IncomingForm = require('formidable').IncomingForm

    var form = new IncomingForm()
    form.uploadDir = 'uploads'
    form.parse(req, function (err, fields, files) {
        if (err) {
            console.log('some error', err)
            res.status(400).send(err)
        } else if (!files.file) {
            console.log('no file received')
            res.status(400).send('no file received')
        } else {
            var file = files.file
            console.log('saved file to', file.path)
            console.log('original name', file.name)
            console.log('type', file.type)
            console.log('size', file.size)
            res.status(400).send(file.path, file.name, file.type, file.size)
        }
    })
});

exports.api = functions.https.onRequest(app)

connect-busboy

const functions = require('firebase-functions');
const express = require('express');
const app = express();

var fs = require('fs');
var busboy = require('connect-busboy');
//...
app.use(busboy()); 
//...
app.post('/upload', function(req, res) {
    var fstream;
    req.pipe(req.busboy);
    req.busboy.on('file', function (fieldname, file, filename) {
        console.log("Uploading: " + filename); 
        res.status('200').send('uploaded')
        // fstream = fs.createWriteStream(__dirname + '/files/' + filename);
        // file.pipe(fstream);
        // fstream.on('close', function () {
        //     res.redirect('back');
        // });
    });
});

exports.api = functions.https.onRequest(app)

express-fileupload

const functions = require('firebase-functions');
const express = require('express');
const fileUpload = require('express-fileupload');
const app = express();

// default options
app.use(fileUpload());

app.post('/upload', function(req, res) {
    console.log('upload initializing')
  if (!req.files)
    return res.status(400).send('No files were uploaded.');

  // The name of the input field (i.e. "sampleFile") is used to retrieve the uploaded file
  let sampleFile = req.files.sampleFile;

  if(req.files){
    console.log('File Uploaded')
    res.status('200').send('File uploaded!');
  }

});

exports.api = functions.https.onRequest(app)

multer

const functions = require('firebase-functions');
const express = require('express'); // call express
const app = express(); // define our app using express
const bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
const router = express.Router();

const multer = require('multer');
var storage = multer.memoryStorage()
var upload = multer({ storage: storage })

const readChunk = require('read-chunk');
const fileType = require('file-type');


var returnSuccess = {
    resultCode: 1,
    resultDesc: '',
    resultData: '',
    dataCount: 0,
    resultAction: '',
    timeStamp: Date.now()
}

var returnFailed = {
    resultCode: -1,
    resultDesc: '',
    resultAction: '',
    timeStamp: Date.now()
}

router.post('/fileChecker', upload.single('doc'), function (req, res, next) {
    console.log('Initilize FileChecker')
    var files = []
    var checkResults = []
    var file = req.file
    files = req.files
    console.log(file)
    console.log(files)
    if(file){
        console.log('got the FILE!')
    }

    res.status(200).send('Processed Ntg')
});


app.use(router)

exports.api = functions.https.onRequest(app)

*Additional Note:

1)For the multer, firebase hosting seems like not compatible with dest option. It will have deployment error if set the dest option.

2)All the code was tested after deployed to firebase hosting, i didn't test on my local.


I am running out of idea whats wrong is going on.. Please help..

Jerry
  • 1,455
  • 1
  • 18
  • 39
  • Sstep 1 is to add some simple error handling to express, and step 2 (if you can't resolve it from the errors (which I'm wildly guessing is EACCES) show us the errors. – ippi Jul 04 '18 at 02:26

2 Answers2

0

Sorry, not a complete answer but too verbose for comments. Just to test that multer is working try a catch-all middleware and put it early in your middleware chain (I have mine after cors-handling but before body-parsing):

onFileUploadStart(file /* , req, res */) { console.log('multer.onFileUploadStart!', file); This should catch absolutely anything that sends a file.

edit: I'm using old versions and deprecated stuff it seems... sorry.


Which when looking at your code one more time... where is your CORS-handling? You need it for browser-requests cross-domain, cross-protocol or even cross-port.

Check here https://enable-cors.org/server_expressjs.html or try the cors package.

or How to allow CORS?

ippi
  • 9,857
  • 2
  • 39
  • 50
  • i bypass the cors, the API was successfully reach the server and returning response. Just the get file code doesn't work – Jerry Jul 04 '18 at 02:56
  • is that right if i implement like this? `var upload = multer({onFileUploadStart(file /* , req, res */) { console.log('multer.onFileUploadStart!', file);}})` and `router.post('/fileChecker', upload.single('doc'), function (req, res, next) {console.log('Initilize FileChecker')})`. If it is correct then the console.log of multer.onFileUploadStart doesn't appear – Jerry Jul 04 '18 at 02:58
  • Ah, I tried that and it didn't work on my set-up. So then I tried looking up `onFileUploadStart` which I'm using and it seems I'm using an old multer version and now `onFileUploadStart` has been deprecated so I'm sorry for misleading you! – ippi Jul 04 '18 at 03:12
  • Is ok, at least you try to help. Thanks anyway :) – Jerry Jul 04 '18 at 03:51
0

Multer: The memory storage engine stores the files in memory as Buffer objects. It doesn't have any options. When using memory storage, the file info will contain a field called buffer that contains the entire file. See docs

Because you're doing memory upload. See if you really want to upload to memory or the disk?

MiKr13
  • 1,297
  • 13
  • 20
  • The file should just keep for a while in order to use it to process by other code, then it can be removed. Based on multer doc, the dest is meaning to upload to memory right? it seems like the firebase function is not compitable with the dest option. It will has deployment issue. – Jerry Jul 04 '18 at 03:01
  • No, memory doesn't need any options. Pls, see the link I attached. Your file is in a buffer. Clearly specified: The memory storage engine stores the files in memory as Buffer objects. "It doesn't have any options." – MiKr13 Jul 04 '18 at 03:05
  • Options are for DiskStorage. – MiKr13 Jul 04 '18 at 03:05
  • additionally, the `buffer` is implement in `req.file.buffer` right? my code can't detect the `req.file` in the first place. – Jerry Jul 04 '18 at 03:06
  • `var uploadFile = multer( {storage: multer.memoryStorage(),}).single('photo');` `var photo = req.files.photo || req.file || null;` and then `var data = photo.buffer.toString('base64');` and check `file.buffer` – MiKr13 Jul 04 '18 at 03:09
  • I using ur code, just modify the 'photo' to 'doc'. But then i sitll hit error: Server log show: `TypeError: Cannot read property 'doc' of undefined` at `var doc = req.files.doc || req.file || null`. Maybe you can check my html code see whether i post it correctly. FYI, i changed the name to "doc" in – Jerry Jul 04 '18 at 03:51
  • `var uploadFile = multer( {storage: multer.memoryStorage(),}).single('doc');` did you do this? I mean change photo in single to doc? – MiKr13 Jul 04 '18 at 07:04
  • ya, at the same time i also changed the html upload into to `` – Jerry Jul 04 '18 at 09:09