Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow uploading media files from file streams #305

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 65 additions & 18 deletions lib/file_uploader.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ var assert = require('assert');
var fs = require('fs');
var mime = require('mime');
var util = require('util');
var request = require('http');
var fileType = require('file-type');

var MAX_FILE_SIZE_BYTES = 15 * 1024 * 1024;
var MAX_FILE_CHUNK_BYTES = 5 * 1024 * 1024;
Expand All @@ -14,17 +16,28 @@ var MAX_FILE_CHUNK_BYTES = 5 * 1024 * 1024;
* console.log(err, bodyObj);
* })
*
* @param {Object} params Object of the form { file_path: String }.
* var request = require('request')
* var stream = request('http://www.domain.com/file_to_upload.ext')
* var fu = new FileUploader({ file_stream: stream }, twit);
* fu.upload(function (err, bodyObj, resp) {
* console.log(err, bodyObj);
* })
*
* @param {Object} params Object of the form { file_path: String } or { file_strea: ReadableStream }
* @param {Twit(object)} twit Twit instance.
*/
var FileUploader = function (params, twit) {
assert(params)
assert(params.file_path, 'Must specify `file_path` to upload a file. Got: ' + params.file_path + '.')
assert(params.file_path || params.file_stream, 'Must specify `file_path` or `file_stream` to upload a file.')
var self = this;
self._file_path = params.file_path;
self._file_stream = params.file_stream;
self._remoteUpload = self._file_stream ? true : false;
self._twit = twit;
self._isUploading = false;
self._isFileStreamEnded = false;

if (self._remoteUpload) {self._file_stream.pause();}
}

/**
Expand All @@ -43,7 +56,7 @@ FileUploader.prototype.upload = function (cb) {
} else {
var mediaTmpId = bodyObj.media_id_string;
var chunkNumber = 0;
var mediaFile = fs.createReadStream(self._file_path, { highWatermark: MAX_FILE_CHUNK_BYTES });
var mediaFile = self._file_stream || fs.createReadStream(self._file_path, { highWatermark: MAX_FILE_CHUNK_BYTES });

mediaFile.on('data', function (chunk) {
// Pause our file stream from emitting `data` events until the upload of this chunk completes.
Expand Down Expand Up @@ -75,6 +88,7 @@ FileUploader.prototype.upload = function (cb) {
self._finalizeMedia(mediaTmpId, cb);
}
});
mediaFile.resume();
}
})
}
Expand Down Expand Up @@ -116,28 +130,61 @@ FileUploader.prototype._appendMedia = function(media_id_string, chunk_part, segm
}, cb);
}

FileUploader.prototype._getFileInfoForUpload = function(cb) {
var self = this;

if (self._remoteUpload === true) {

request.get(self._file_stream.uri.href, function(res){
res.once('data', function(chunk){

var len = res.headers['content-length']
if (!len) {
return cb(new Error('Unable to determine file size'))
}
len = +len
if (len !== len) {
return cb(new Error('Invalid Content-Length received'))
}

var mediaFileSizeBytes = +res.headers['content-length']
var mediaType = fileType(chunk)["mime"];

cb(null, mediaType, mediaFileSizeBytes);
res.destroy();
});
});
}
else {
var mediaType = mime.lookup(self._file_path);
var mediaFileSizeBytes = fs.statSync(self._file_path).size;
cb(null, mediaType, mediaFileSizeBytes);
}
}

/**
* Send INIT command for our underlying media object.
*
* @param {Function} cb
*/
FileUploader.prototype._initMedia = function (cb) {
var self = this;
var mediaType = mime.lookup(self._file_path);
var mediaFileSizeBytes = fs.statSync(self._file_path).size;

// Check the file size - it should not go over 15MB for video.
// See https://dev.twitter.com/rest/reference/post/media/upload-chunked
if (mediaFileSizeBytes < MAX_FILE_SIZE_BYTES) {
self._twit.post('media/upload', {
'command': 'INIT',
'media_type': mediaType,
'total_bytes': mediaFileSizeBytes
}, cb);
} else {
var errMsg = util.format('This file is too large. Max size is %dB. Got: %dB.', MAX_FILE_SIZE_BYTES, mediaFileSizeBytes);
cb(new Error(errMsg));
}

self._getFileInfoForUpload(function(err, mediaType, mediaFileSizeBytes){

// Check the file size - it should not go over 15MB for video.
// See https://dev.twitter.com/rest/reference/post/media/upload-chunked
if (mediaFileSizeBytes < MAX_FILE_SIZE_BYTES) {
self._twit.post('media/upload', {
'command': 'INIT',
'media_type': mediaType,
'total_bytes': mediaFileSizeBytes
}, cb);
} else {
var errMsg = util.format('This file is too large. Max size is %dB. Got: %dB.', MAX_FILE_SIZE_BYTES, mediaFileSizeBytes);
cb(new Error(errMsg));
}
});
}

module.exports = FileUploader
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
],
"dependencies": {
"bluebird": "^3.1.5",
"file-type": "^3.9.0",
"mime": "^1.3.4",
"request": "^2.68.0"
},
Expand Down