99 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			99 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| var readFile = require('fs').readFile,
 | |
|     basename = require('path').basename;
 | |
| 
 | |
| exports.build = function(data, boundary, callback) {
 | |
| 
 | |
|   if (typeof data != 'object' || typeof data.pipe == 'function')
 | |
|     return callback(new Error('Multipart builder expects data as key/val object.'));
 | |
| 
 | |
|   var body   = '',
 | |
|       object = flatten(data),
 | |
|       count  = Object.keys(object).length;
 | |
| 
 | |
|   if (count === 0)
 | |
|     return callback(new Error('Empty multipart body. Invalid data.'))
 | |
| 
 | |
|   function done(err, section) {
 | |
|     if (err) return callback(err);
 | |
|     if (section) body += section;
 | |
|     --count || callback(null, body + '--' + boundary + '--');
 | |
|   };
 | |
| 
 | |
|   for (var key in object) {
 | |
|     var value = object[key];
 | |
|     if (value === null || typeof value == 'undefined') {
 | |
|       done();
 | |
|     } else if (Buffer.isBuffer(value)) {
 | |
|       var part = { buffer: value, content_type: 'application/octet-stream' };
 | |
|       generate_part(key, part, boundary, done);
 | |
|     } else {
 | |
|       var part = (value.buffer || value.file || value.content_type) ? value : { value: value };
 | |
|       generate_part(key, part, boundary, done);
 | |
|     }
 | |
|   }
 | |
| 
 | |
| }
 | |
| 
 | |
| function generate_part(name, part, boundary, callback) {
 | |
| 
 | |
|   var return_part = '--' + boundary + '\r\n';
 | |
|   return_part += 'Content-Disposition: form-data; name="' + name + '"';
 | |
| 
 | |
|   function append(data, filename) {
 | |
| 
 | |
|     if (data) {
 | |
|       var binary = part.content_type.indexOf('text') == -1;
 | |
|       return_part += '; filename="' + encodeURIComponent(filename) + '"\r\n';
 | |
|       if (binary) return_part += 'Content-Transfer-Encoding: binary\r\n';
 | |
|       return_part += 'Content-Type: ' + part.content_type + '\r\n\r\n';
 | |
|       return_part += binary ? data.toString('binary') : data.toString('utf8');
 | |
|     }
 | |
| 
 | |
|     callback(null, return_part + '\r\n');
 | |
|   };
 | |
| 
 | |
|   if ((part.file || part.buffer) && part.content_type) {
 | |
| 
 | |
|     var filename = part.filename ? part.filename : part.file ? basename(part.file) : name;
 | |
|     if (part.buffer) return append(part.buffer, filename);
 | |
| 
 | |
|     readFile(part.file, function(err, data) {
 | |
|       if (err) return callback(err);
 | |
|       append(data, filename);
 | |
|     });
 | |
| 
 | |
|   } else {
 | |
| 
 | |
|     if (typeof part.value == 'object')
 | |
|       return callback(new Error('Object received for ' + name + ', expected string.'))
 | |
| 
 | |
|     if (part.content_type) {
 | |
|       return_part += '\r\n';
 | |
|       return_part += 'Content-Type: ' + part.content_type;
 | |
|     }
 | |
| 
 | |
|     return_part += '\r\n\r\n';
 | |
|     return_part += Buffer.from(String(part.value), 'utf8').toString('binary');
 | |
|     append();
 | |
| 
 | |
|   }
 | |
| 
 | |
| }
 | |
| 
 | |
| // flattens nested objects for multipart body
 | |
| function flatten(object, into, prefix) {
 | |
|   into = into || {};
 | |
| 
 | |
|   for(var key in object) {
 | |
|     var prefix_key = prefix ? prefix + '[' + key + ']' : key;
 | |
|     var prop = object[key];
 | |
| 
 | |
|     if (prop && typeof prop === 'object' && !(prop.buffer || prop.file || prop.content_type))
 | |
|       flatten(prop, into, prefix_key)
 | |
|     else
 | |
|       into[prefix_key] = prop;
 | |
|   }
 | |
| 
 | |
|   return into;
 | |
| }
 |