Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Form/file uploads with hapi.js

Demo of multipart form/file uploading with hapi.js.

Usage

npm install
npm run setup
npm run server

Then ...

npm run test

... or try:

curl --form file=@data.csv    \
     --form firstName=Melvin  \
     --form lastName=Mooney   \
     http://localhost:8080/submit

See also

Other multipart/form-data demos:

A B C
a b c
1 2 3
{
"name": "file-upload-demo",
"version": "0.0.0",
"description": "multipart form/file upload demo with hapi.js",
"main": "server.js",
"scripts": {
"setup": "[ -d uploads ] || mkdir uploads",
"test": "curl --form file=@data.csv --form user=Jones http://localhost:8080/submit",
"server": "node server.js"
},
"keywords": [
"multipart",
"upload",
"file",
"form",
"hapi",
"demo"
],
"author": "J. Voigt",
"license": "BSD-2-Clause",
"dependencies": {
"hapi": "~6.8.0"
}
}
var fs = require('fs');
var Hapi = require('hapi');
var server = Hapi.createServer('localhost', Number(process.argv[2] || 8080));
server.route({
method: 'POST',
path: '/submit',
config: {
payload: {
output: 'stream',
parse: true,
allow: 'multipart/form-data'
},
handler: function (request, reply) {
var data = request.payload;
if (data.file) {
var name = data.file.hapi.filename;
var path = __dirname + "/uploads/" + name;
var file = fs.createWriteStream(path);
file.on('error', function (err) {
console.error(err)
});
data.file.pipe(file);
data.file.on('end', function (err) {
var ret = {
filename: data.file.hapi.filename,
headers: data.file.hapi.headers
}
reply(JSON.stringify(ret));
})
}
}
}
});
server.start(function () {
console.log('info', 'Server running at: ' + server.info.uri);
});
@codeofnode

This comment has been minimized.

Show comment Hide comment
@codeofnode

codeofnode Oct 31, 2015

i use the exact same code and i am getting request.payload.file as undefined in handler.

i use the exact same code and i am getting request.payload.file as undefined in handler.

@Gusbenz

This comment has been minimized.

Show comment Hide comment
@Gusbenz

Gusbenz Nov 3, 2015

Thanks for this! Works great!

Gusbenz commented Nov 3, 2015

Thanks for this! Works great!

@luismanolo

This comment has been minimized.

Show comment Hide comment
@luismanolo

luismanolo Dec 30, 2015

Many thanks.

Many thanks.

@gino8080

This comment has been minimized.

Show comment Hide comment
@gino8080

gino8080 Jan 14, 2016

great thank you!

how to track progress of the upload? bytes loaded / bytes total

great thank you!

how to track progress of the upload? bytes loaded / bytes total

@mofengfly

This comment has been minimized.

Show comment Hide comment
@mofengfly

mofengfly Mar 2, 2016

thks

thks

@notyalca

This comment has been minimized.

Show comment Hide comment
@notyalca

notyalca Apr 15, 2016

Yeah, thanks for this. Nice and concise.

Yeah, thanks for this. Nice and concise.

@behnoodk

This comment has been minimized.

Show comment Hide comment
@behnoodk

behnoodk Apr 19, 2016

thank you for this!

thank you for this!

@Jportofolio

This comment has been minimized.

Show comment Hide comment
@Jportofolio

Jportofolio May 23, 2016

Thanks !
Would you give you advice about iterating through a FileList object and write each one of them async ?

Jportofolio commented May 23, 2016

Thanks !
Would you give you advice about iterating through a FileList object and write each one of them async ?

@Jportofolio

This comment has been minimized.

Show comment Hide comment
@Jportofolio

Jportofolio May 24, 2016

I finally get how to to receive multiple text and files here the code:

const Home = {
files: {
relativeTo: Path.join(__dirname, '../static/src')
},
handler: (req, res) => {
var data = req.payload;
for(var i = 0; i < Object.keys(data).length; i++){
if (data.hasOwnProperty('file-'+i)){
var g = "file-"+i;
var name = data[g].hapi.filename;
console.log(name);
var path = Path.join(__dirname, '../uploadFiles/'+name);
var file = Fs.createWriteStream(path);

       file.on('error', function (err){
           console.error(err);
       });
       data[g].pipe(file);

       data[g].on('end', function (err){

           setTimeout(function(){
               var ret = {
               filename: data[g].hapi.filename,
               headers: data[g].hapi.headers
           }
           res(JSON.stringify(ret));
           }, 200);
       })
       }

   }

},

payload: {
    output : 'stream',
    parse: true,
    uploads: 'up_files',
    timeout: 30034,
    // allow: 'multipart/form-data',
    failAction: 'log',
    maxBytes: 3000000
}

};

//Root Loading
const Root = {
files: {
relativeTo: Path.join(__dirname, '../static/src')
},
handler: (req, res) => {
res.file('index.html');
}
}

module.exports = [
{ method: 'GET', path:'/{static*}', config: Static },
{ method: 'POST', path: '/home', config: Home },
{ method: 'GET', path: '/', config: Root }
]

I finally get how to to receive multiple text and files here the code:

const Home = {
files: {
relativeTo: Path.join(__dirname, '../static/src')
},
handler: (req, res) => {
var data = req.payload;
for(var i = 0; i < Object.keys(data).length; i++){
if (data.hasOwnProperty('file-'+i)){
var g = "file-"+i;
var name = data[g].hapi.filename;
console.log(name);
var path = Path.join(__dirname, '../uploadFiles/'+name);
var file = Fs.createWriteStream(path);

       file.on('error', function (err){
           console.error(err);
       });
       data[g].pipe(file);

       data[g].on('end', function (err){

           setTimeout(function(){
               var ret = {
               filename: data[g].hapi.filename,
               headers: data[g].hapi.headers
           }
           res(JSON.stringify(ret));
           }, 200);
       })
       }

   }

},

payload: {
    output : 'stream',
    parse: true,
    uploads: 'up_files',
    timeout: 30034,
    // allow: 'multipart/form-data',
    failAction: 'log',
    maxBytes: 3000000
}

};

//Root Loading
const Root = {
files: {
relativeTo: Path.join(__dirname, '../static/src')
},
handler: (req, res) => {
res.file('index.html');
}
}

module.exports = [
{ method: 'GET', path:'/{static*}', config: Static },
{ method: 'POST', path: '/home', config: Home },
{ method: 'GET', path: '/', config: Root }
]

@MaySnow

This comment has been minimized.

Show comment Hide comment
@MaySnow

MaySnow Jul 18, 2016

Thanks!

MaySnow commented Jul 18, 2016

Thanks!

@madrus

This comment has been minimized.

Show comment Hide comment
@madrus

madrus Jul 25, 2016

Works like a charm. Thanks.

madrus commented Jul 25, 2016

Works like a charm. Thanks.

@moogway

This comment has been minimized.

Show comment Hide comment
@moogway

moogway Sep 23, 2016

Hi,

Thanks for the code. I have a question though.

Instead of using data.file.on('end',...) as the trigger to send response, wouldn't it be better to use file.on('finish',...) as the trigger? Would it ensure that the buffer stream has indeed been written into the file before a response is sent?

Could you have time to discuss this?

Thanks

moogway commented Sep 23, 2016

Hi,

Thanks for the code. I have a question though.

Instead of using data.file.on('end',...) as the trigger to send response, wouldn't it be better to use file.on('finish',...) as the trigger? Would it ensure that the buffer stream has indeed been written into the file before a response is sent?

Could you have time to discuss this?

Thanks

@daslicht

This comment has been minimized.

Show comment Hide comment
@daslicht

daslicht Jan 19, 2017

so uploading files in haps works without any external module (as in exporess) ?

so uploading files in haps works without any external module (as in exporess) ?

@dgfreecodecamper

This comment has been minimized.

Show comment Hide comment
@dgfreecodecamper

dgfreecodecamper Jul 29, 2017

Thank you for the code above - works fine with the curl command when I test. However, when I try to use it from an HTML file the server just hangs? /submit is the url and testcsvfile.csv is the file I am trying to upload. Can I not simply use an HTML form as below?

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

I also tried making the call using postman which received the following response

{
    "statusCode": 415,
    "error": "Unsupported Media Type",
    "message": "Unsupported Media Type"
}

The file is a simple csv text file? Any ideas would be helpful?

Thank you for the code above - works fine with the curl command when I test. However, when I try to use it from an HTML file the server just hangs? /submit is the url and testcsvfile.csv is the file I am trying to upload. Can I not simply use an HTML form as below?

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

I also tried making the call using postman which received the following response

{
    "statusCode": 415,
    "error": "Unsupported Media Type",
    "message": "Unsupported Media Type"
}

The file is a simple csv text file? Any ideas would be helpful?

@Stephiev

This comment has been minimized.

Show comment Hide comment
@Stephiev

Stephiev Aug 30, 2017

I'm using content-type as application/octet-stream and data.file is undefined. Any ideas? I'm using application/octet-stream as the service i'm going to pass the file to needs that, not quite sure if having application/octet-stream is required in the hapi path?

I'm using content-type as application/octet-stream and data.file is undefined. Any ideas? I'm using application/octet-stream as the service i'm going to pass the file to needs that, not quite sure if having application/octet-stream is required in the hapi path?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment