Skip to content

Commit

Permalink
https://github.com/danwrong/restler/issues/250
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael Jedich committed Mar 4, 2018
1 parent f31e457 commit b261064
Showing 1 changed file with 174 additions and 168 deletions.
342 changes: 174 additions & 168 deletions lib/multipartform.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,205 +6,211 @@ 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;
//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));
}
//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';
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) {
var parts = path.split(/\/|\\/);
return parts[parts.length - 1];
}
_basename: function(path) {
var 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;
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;
this.name = name;
this.value = value;
this.boundary = boundary;
}


Part.prototype = {
//returns the Content-Disposition header
header: function() {
var 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() {
var valueSize;
if (this.value instanceof File) {
valueSize = this.value.fileSize;
} else if (this.value.data) {
valueSize = this.value.data.length;

//returns the Content-Disposition header
header: function() {
var 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() {
var 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) {

var 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", 0666, function (err, fd) {
if (err) throw err;

var position = 0;

(function reader () {
fs.read(fd, 1024 * 4, position, "binary", function (er, chunk) {
if (er) callback(err);
stream.write(chunk);
position += 1024 * 4;
if (chunk) 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();
}
}
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) {
var 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+', function (err, fd) {
var stats = fs.fstatSync(fd);
var bufferSize = stats.size
if (err) throw err;
var chunkSize = 512;
var position = 0;
var buf = new Buffer(bufferSize);
(function reader () {

if ((position + chunkSize) > bufferSize) {
chunkSize = (bufferSize - position);
}

fs.read(fd, buf, position, chunkSize, position, function (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();
this.encoding = 'binary';
this.boundary = boundary || exports.defaultBoundary;
this.data = data;
this.partNames = this._partNames();
}

MultiPartRequest.prototype = {
_partNames: function() {
var partNames = [];
for (var name in this.data) {
partNames.push(name)
}
return partNames;
},
write: function(stream, callback) {
var 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() {
var partName = self.partNames[partCount];
var part = new Part(partName, self.data[partName], self.boundary);
part.write(stream, function (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 || "");
}
});
})();
}
_partNames: function() {
var partNames = [];
for (var name in this.data) {
partNames.push(name)
}
return partNames;
},

write: function(stream, callback) {
var 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() {
var partName = self.partNames[partCount];
var part = new Part(partName, self.data[partName], self.boundary);
part.write(stream, function (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 || "");
}
});
})();
}
}

var 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) {
var totalSize = 0;
boundary = boundary || exports.defaultBoundary;
for (var name in parts) totalSize += new Part(name, parts[name], boundary).sizeOf();
return totalSize + boundary.length + 6;
},
write: function(stream, data, callback, boundary) {
var r = new MultiPartRequest(data, boundary);
r.write(stream, callback);
return r;
}
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) {
var totalSize = 0;
boundary = boundary || exports.defaultBoundary;
for (var name in parts) totalSize += new Part(name, parts[name], boundary).sizeOf();
return totalSize + boundary.length + 6;
},
write: function(stream, data, callback, boundary) {
var r = new MultiPartRequest(data, boundary);
r.write(stream, callback);
return r;
}
}

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

0 comments on commit b261064

Please sign in to comment.