Skip to content

Instantly share code, notes, and snippets.

@joyrexus
Last active August 3, 2023 22:56
Show Gist options
  • Star 56 You must be signed in to star a gist
  • Fork 20 You must be signed in to fork a gist
  • Save joyrexus/0c6bd5135d7edeba7b87 to your computer and use it in GitHub Desktop.
Save joyrexus/0c6bd5135d7edeba7b87 to your computer and use it in GitHub Desktop.
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": "^17.5.4"
}
}
const fs = require('fs');
const Hapi = require('hapi');
const server = Hapi.server({
host: 'localhost',
port: Number(process.argv[2] || 8080)
});
server.route({
method: 'POST',
path: '/submit',
handler: (request, h) => {
const data = request.payload;
if (data.file) {
const name = data.file.hapi.filename;
const path = __dirname + "/uploads/" + name;
const file = fs.createWriteStream(path);
file.on('error', (err) => console.error(err));
data.file.pipe(file);
data.file.on('end', (err) => {
const ret = {
filename: data.file.hapi.filename,
headers: data.file.hapi.headers
}
return JSON.stringify(ret);
})
}
return 'ok';
},
options: {
payload: {
output: 'stream',
parse: true,
allow: 'multipart/form-data'
}
}
});
const init = async () => {
await server.start();
console.log(`Server running at: ${server.info.uri}`);
};
init();
@luismanolo
Copy link

Many thanks.

@gino8080
Copy link

great thank you!

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

@mofengfly
Copy link

thks

@notyalca
Copy link

Yeah, thanks for this. Nice and concise.

@behnoodk
Copy link

thank you for this!

@jkula-c
Copy link

jkula-c commented May 23, 2016

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

@jkula-c
Copy link

jkula-c commented 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 }
]

@MaySnow
Copy link

MaySnow commented Jul 18, 2016

Thanks!

@madrus
Copy link

madrus commented Jul 25, 2016

Works like a charm. Thanks.

@moogway
Copy link

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
Copy link

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

@dgfreecodecamper
Copy link

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
Copy link

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?

@bilalk1
Copy link

bilalk1 commented Mar 18, 2019

It is very helping!

@yogesh-xseed
Copy link

if the file size is less then 15 kb its not working

@jbarros35
Copy link

most of these old samples wont compile anymore.

@aishadeshmukh
Copy link

Thank you so much the demo. It really helped 👍

@riteshkkr
Copy link

updated code

	{
		method: "POST",
		path: "/upload",
		handler: clientController.postClientFile,
		config: {
			description: "Home",
			auth: false,
			payload: {
				maxBytes: 209715200,
				parse: true,
				allow: "multipart/form-data",
				multipart: { output: "stream" },
			},
		},
	},

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