Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
LittleWhiteYA committed May 26, 2018
1 parent d474f8a commit b813ce9
Showing 1 changed file with 218 additions and 0 deletions.
218 changes: 218 additions & 0 deletions multipartform.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
/* eslint-disable */
const fs = require('fs');
const sys = require('util');

exports.defaultBoundary = '48940923NODERESLTER3890457293';

// This little object allows us hijack the write method via duck-typing
// and write to strings or regular streams that support the write method.
function Stream(stream) {
// If the user pases a string for stream,we initalize one to write to
if (this._isString(stream)) {
this.string = '';
}
this.stream = stream;
}

Stream.prototype = {
// write to an internal String or to the Stream
write: function(data) {
if (this.string != undefined) {
this.string += data;
} else {
this.stream.write(data, 'binary');
}
},

// stolen from underscore.js
_isString: function(obj) {
return !!(obj === '' || (obj && obj.charCodeAt && obj.substr));
},
};

function File(path, filename, fileSize, encoding, contentType) {
this.path = path;
this.filename = filename || this._basename(path);
this.fileSize = fileSize;
this.encoding = encoding || 'binary';
this.contentType = contentType || 'application/octet-stream';
}

File.prototype = {
_basename: function(path) {
const parts = path.split(/\/|\\/);
return parts[parts.length - 1];
},
};

function Data(filename, contentType, data) {
this.filename = filename;
this.contentType = contentType || 'application/octet-stream';
this.data = data;
}

function Part(name, value, boundary) {
this.name = name;
this.value = value;
this.boundary = boundary;
}

Part.prototype = {
// returns the Content-Disposition header
header: function() {
let header;

if (this.value.data) {
header =
`Content-Disposition: form-data; name="${this.name}"; filename="${
this.value.filename
}"\r\n` +
`Content-Length: ${this.value.data.length}\r\n` +
`Content-Type: ${this.value.contentType}`;
} else if (this.value instanceof File) {
header =
`Content-Disposition: form-data; name="${this.name}"; filename="${
this.value.filename
}"\r\n` +
`Content-Length: ${this.value.fileSize}\r\n` +
`Content-Type: ${this.value.contentType}`;
} else {
header = `Content-Disposition: form-data; name="${this.name}"`;
}

return `--${this.boundary}\r\n${header}\r\n\r\n`;
},

// calculates the size of the Part
sizeOf: function() {
let valueSize;
if (this.value instanceof File) {
valueSize = this.value.fileSize;
} else if (this.value.data) {
valueSize = this.value.data.length;
} else if (typeof this.value === 'number') {
valueSize = this.value.toString().length;
} else {
valueSize = this.value.length;
}
return valueSize + this.header().length + 2;
},

// Writes the Part out to a writable stream that supports the write(data) method
// You can also pass in a String and a String will be returned to the callback
// with the whole Part
// Calls the callback when complete
write: function(stream, callback) {
const self = this;

// first write the Content-Disposition
stream.write(this.header());

// Now write out the body of the Part
if (this.value instanceof File) {
fs.open(this.value.path, 'r+', (err, fd) => {
const stats = fs.fstatSync(fd);
const bufferSize = stats.size;
if (err) throw err;
let chunkSize = 512;
let position = 0;
const buf = new Buffer(bufferSize);
(function reader() {
if (position + chunkSize > bufferSize) {
chunkSize = bufferSize - position;
}

fs.read(fd, buf, position, chunkSize, position, (er, chunk, buf) => {
if (er) callback(err);
stream.write(buf.slice(position, chunkSize + position));
position += chunkSize;
if (position < bufferSize) reader();
else {
stream.write('\r\n');
callback();
fs.close(fd);
}
});
})(); // reader()
});
} else if (this.value instanceof Data) {
stream.write(this.value.data);
stream.write('\r\n');
callback();
} else {
stream.write(`${this.value}\r\n`);
callback();
}
},
};

// Renamed to MultiPartRequest from Request
function MultiPartRequest(data, boundary) {
this.encoding = 'binary';
this.boundary = boundary || exports.defaultBoundary;
this.data = data;
this.partNames = this._partNames();
}

MultiPartRequest.prototype = {
_partNames: function() {
const partNames = [];
for (const name in this.data) {
partNames.push(name);
}
return partNames;
},

write: function(stream, callback) {
let partCount = 0,
self = this;

// wrap the stream in our own Stream object
// See the Stream function above for the benefits of this
var stream = new Stream(stream);

// Let each part write itself out to the stream
(function writePart() {
const partName = self.partNames[partCount];
const part = new Part(partName, self.data[partName], self.boundary);
part.write(stream, err => {
if (err) {
callback(err);
return;
}
partCount += 1;
if (partCount < self.partNames.length) writePart();
else {
stream.write(`--${self.boundary}--` + `\r\n`);

if (callback) callback(stream.string || '');
}
});
})();
},
};

const exportMethods = {
file: function(path, filename, fileSize, encoding, contentType) {
return new File(path, filename, fileSize, encoding, contentType);
},
data: function(filename, contentType, data) {
return new Data(filename, contentType, data);
},
sizeOf: function(parts, boundary) {
let totalSize = 0;
boundary = boundary || exports.defaultBoundary;
for (const name in parts)
totalSize += new Part(name, parts[name], boundary).sizeOf();
return totalSize + boundary.length + 6;
},
write: function(stream, data, callback, boundary) {
const r = new MultiPartRequest(data, boundary);
r.write(stream, callback);
return r;
},
};

Object.keys(exportMethods).forEach(exportMethod => {
exports[exportMethod] = exportMethods[exportMethod];
});

0 comments on commit b813ce9

Please sign in to comment.