-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d474f8a
commit b813ce9
Showing
1 changed file
with
218 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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]; | ||
}); |