import stream from 'stream'; import fs from 'fs'; import path from 'path'; import { Meteor } from 'meteor/meteor'; import { MongoInternals } from 'meteor/mongo'; import Grid from 'gridfs-stream'; import mkdirp from 'mkdirp'; // Fix problem with usernames being converted to object id Grid.prototype.tryParseObjectId = function() { return false; }; const RocketChatFile = {}; RocketChatFile.bufferToStream = function(buffer) { const bufferStream = new stream.PassThrough(); bufferStream.end(buffer); return bufferStream; }; RocketChatFile.dataURIParse = function(dataURI) { const imageData = dataURI.split(';base64,'); return { image: imageData[1], contentType: imageData[0].replace('data:', ''), }; }; RocketChatFile.addPassThrough = function(st, fn) { const pass = new stream.PassThrough(); fn(pass, st); return pass; }; RocketChatFile.GridFS = class { constructor(config = {}) { const { name = 'file', transformWrite } = config; this.name = name; this.transformWrite = transformWrite; const mongo = MongoInternals.NpmModule; const { db } = MongoInternals.defaultRemoteCollectionDriver().mongo; this.store = new Grid(db, mongo); this.findOneSync = Meteor.wrapAsync(this.store.collection(this.name).findOne.bind(this.store.collection(this.name))); this.removeSync = Meteor.wrapAsync(this.store.remove.bind(this.store)); this.countSync = Meteor.wrapAsync(this.store._col.count.bind(this.store._col)); this.getFileSync = Meteor.wrapAsync(this.getFile.bind(this)); } findOne(fileName) { return this.findOneSync({ _id: fileName, }); } remove(fileName) { return this.removeSync({ _id: fileName, root: this.name, }); } createWriteStream(fileName, contentType) { const self = this; let ws = this.store.createWriteStream({ _id: fileName, filename: fileName, mode: 'w', root: this.name, content_type: contentType, }); if (self.transformWrite != null) { ws = RocketChatFile.addPassThrough(ws, function(rs, ws) { const file = { name: self.name, fileName, contentType, }; return self.transformWrite(file, rs, ws); }); } ws.on('close', function() { return ws.emit('end'); }); return ws; } createReadStream(fileName) { return this.store.createReadStream({ _id: fileName, root: this.name, }); } getFileWithReadStream(fileName) { const file = this.findOne(fileName); if (file == null) { return null; } const rs = this.createReadStream(fileName); return { readStream: rs, contentType: file.contentType, length: file.length, uploadDate: file.uploadDate, }; } getFile(fileName, cb) { const file = this.getFileWithReadStream(fileName); if (!file) { return cb(); } const data = []; file.readStream.on('data', Meteor.bindEnvironment(function(chunk) { return data.push(chunk); })); return file.readStream.on('end', Meteor.bindEnvironment(function() { return cb(null, { buffer: Buffer.concat(data), contentType: file.contentType, length: file.length, uploadDate: file.uploadDate, }); })); } deleteFile(fileName) { const file = this.findOne(fileName); if (file == null) { return undefined; } return this.remove(fileName); } }; RocketChatFile.FileSystem = class { constructor(config = {}) { let { absolutePath = '~/uploads' } = config; const { transformWrite } = config; this.transformWrite = transformWrite; if (absolutePath.split(path.sep)[0] === '~') { const homepath = process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE; if (homepath != null) { absolutePath = absolutePath.replace('~', homepath); } else { throw new Error('Unable to resolve "~" in path'); } } this.absolutePath = path.resolve(absolutePath); mkdirp.sync(this.absolutePath); this.statSync = Meteor.wrapAsync(fs.stat.bind(fs)); this.unlinkSync = Meteor.wrapAsync(fs.unlink.bind(fs)); this.getFileSync = Meteor.wrapAsync(this.getFile.bind(this)); } createWriteStream(fileName, contentType) { const self = this; let ws = fs.createWriteStream(path.join(this.absolutePath, fileName)); if (self.transformWrite != null) { ws = RocketChatFile.addPassThrough(ws, function(rs, ws) { const file = { fileName, contentType, }; return self.transformWrite(file, rs, ws); }); } ws.on('close', function() { return ws.emit('end'); }); return ws; } createReadStream(fileName) { return fs.createReadStream(path.join(this.absolutePath, fileName)); } stat(fileName) { return this.statSync(path.join(this.absolutePath, fileName)); } remove(fileName) { return this.unlinkSync(path.join(this.absolutePath, fileName)); } getFileWithReadStream(fileName) { try { const stat = this.stat(fileName); const rs = this.createReadStream(fileName); return { readStream: rs, // contentType: file.contentType length: stat.size, }; } catch (error1) { return null; } } getFile(fileName, cb) { const file = this.getFileWithReadStream(fileName); if (!file) { return cb(); } const data = []; file.readStream.on('data', Meteor.bindEnvironment(function(chunk) { return data.push(chunk); })); return file.readStream.on('end', Meteor.bindEnvironment(function() { return { buffer: Buffer.concat(data)({ contentType: file.contentType, length: file.length, uploadDate: file.uploadDate, }), }; })); } deleteFile(fileName) { try { return this.remove(fileName); } catch (error1) { return null; } } }; export { RocketChatFile };