From bb1dd900359d58ac80527d3106e3252c29ff83ea Mon Sep 17 00:00:00 2001 From: joey patino Date: Sat, 22 Oct 2016 18:46:50 +0200 Subject: [PATCH] Allow uploading media files from file streams --- lib/file_uploader.js | 83 ++++++++++++++++++++++++++++++++++---------- package.json | 1 + 2 files changed, 66 insertions(+), 18 deletions(-) diff --git a/lib/file_uploader.js b/lib/file_uploader.js index 6804c490..f57362eb 100644 --- a/lib/file_uploader.js +++ b/lib/file_uploader.js @@ -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; @@ -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();} } /** @@ -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. @@ -75,6 +88,7 @@ FileUploader.prototype.upload = function (cb) { self._finalizeMedia(mediaTmpId, cb); } }); + mediaFile.resume(); } }) } @@ -116,6 +130,38 @@ 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. * @@ -123,21 +169,22 @@ FileUploader.prototype._appendMedia = function(media_id_string, chunk_part, segm */ 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 diff --git a/package.json b/package.json index 66da3c16..14040c53 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ ], "dependencies": { "bluebird": "^3.1.5", + "file-type": "^3.9.0", "mime": "^1.3.4", "request": "^2.68.0" },