Merge pull request #910 from syuilo/use-stream-for-files
file - use readableStream
This commit is contained in:
		
						commit
						0079a88433
					
				
					 1 changed files with 65 additions and 52 deletions
				
			
		| 
						 | 
				
			
			@ -8,6 +8,7 @@ import * as bodyParser from 'body-parser';
 | 
			
		|||
import * as cors from 'cors';
 | 
			
		||||
import * as mongodb from 'mongodb';
 | 
			
		||||
import * as gm from 'gm';
 | 
			
		||||
import * as stream from 'stream';
 | 
			
		||||
 | 
			
		||||
import DriveFile, { getGridFSBucket } from '../api/models/drive-file';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -33,60 +34,81 @@ app.get('/', (req, res) => {
 | 
			
		|||
});
 | 
			
		||||
 | 
			
		||||
app.get('/default-avatar.jpg', (req, res) => {
 | 
			
		||||
	// TODO: 非同期にしたい。Promise対応してないんだろうか...
 | 
			
		||||
	const file = fs.readFileSync(`${__dirname}/assets/avatar.jpg`);
 | 
			
		||||
	const file = fs.createReadStream(`${__dirname}/assets/avatar.jpg`);
 | 
			
		||||
	send(file, 'image/jpeg', req, res);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
app.get('/app-default.jpg', (req, res) => {
 | 
			
		||||
	// TODO: 非同期にしたい。Promise対応してないんだろうか...
 | 
			
		||||
	const file = fs.readFileSync(`${__dirname}/assets/dummy.png`);
 | 
			
		||||
	const file = fs.createReadStream(`${__dirname}/assets/dummy.png`);
 | 
			
		||||
	send(file, 'image/png', req, res);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
async function raw(data: Buffer, type: string, download: boolean, res: express.Response): Promise<any> {
 | 
			
		||||
	res.header('Content-Type', type);
 | 
			
		||||
 | 
			
		||||
	if (download) {
 | 
			
		||||
		res.header('Content-Disposition', 'attachment');
 | 
			
		||||
interface ISend {
 | 
			
		||||
	contentType: string;
 | 
			
		||||
	stream: stream.Readable;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	res.send(data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function thumbnail(data: Buffer, type: string, resize: number, res: express.Response): Promise<any> {
 | 
			
		||||
function thumbnail(data: stream.Readable, type: string, resize: number): ISend {
 | 
			
		||||
		const readable: stream.Readable = (() => {
 | 
			
		||||
			if (!/^image\/.*$/.test(type)) {
 | 
			
		||||
		// TODO: 非同期にしたい。Promise対応してないんだろうか...
 | 
			
		||||
		data = fs.readFileSync(`${__dirname}/assets/not-an-image.png`);
 | 
			
		||||
				// 使わないことにしたストリームはしっかり取り壊しておく
 | 
			
		||||
				data.destroy();
 | 
			
		||||
				return fs.createReadStream(`${__dirname}/assets/not-an-image.png`);
 | 
			
		||||
			}
 | 
			
		||||
			return data;
 | 
			
		||||
		})();
 | 
			
		||||
 | 
			
		||||
	let g = gm(data);
 | 
			
		||||
		let g = gm(readable);
 | 
			
		||||
 | 
			
		||||
		if (resize) {
 | 
			
		||||
			g = g.resize(resize, resize);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	g
 | 
			
		||||
		const stream = g
 | 
			
		||||
			.compress('jpeg')
 | 
			
		||||
			.quality(80)
 | 
			
		||||
		.toBuffer('jpeg', (err, img) => {
 | 
			
		||||
			if (err !== undefined && err !== null) {
 | 
			
		||||
				console.error(err);
 | 
			
		||||
				res.sendStatus(500);
 | 
			
		||||
				return;
 | 
			
		||||
			.stream();
 | 
			
		||||
 | 
			
		||||
		return {
 | 
			
		||||
			contentType: 'image/jpeg',
 | 
			
		||||
			stream
 | 
			
		||||
		};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
			res.header('Content-Type', 'image/jpeg');
 | 
			
		||||
			res.send(img);
 | 
			
		||||
		});
 | 
			
		||||
}
 | 
			
		||||
const commonReadableHandlerGenerator = (req: express.Request, res: express.Response) => (e: Error): void => {
 | 
			
		||||
	console.dir(e);
 | 
			
		||||
	req.destroy();
 | 
			
		||||
	res.destroy(e);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function send(data: Buffer, type: string, req: express.Request, res: express.Response): void {
 | 
			
		||||
function send(readable: stream.Readable, type: string, req: express.Request, res: express.Response): void {
 | 
			
		||||
	readable.on('error', commonReadableHandlerGenerator(req, res));
 | 
			
		||||
 | 
			
		||||
	const data = ((): ISend => {
 | 
			
		||||
		if (req.query.thumbnail !== undefined) {
 | 
			
		||||
		thumbnail(data, type, req.query.size, res);
 | 
			
		||||
	} else {
 | 
			
		||||
		raw(data, type, req.query.download !== undefined, res);
 | 
			
		||||
			return thumbnail(readable, type, req.query.size);
 | 
			
		||||
		}
 | 
			
		||||
		return {
 | 
			
		||||
			contentType: type,
 | 
			
		||||
			stream: readable
 | 
			
		||||
		};
 | 
			
		||||
	})();
 | 
			
		||||
 | 
			
		||||
	if (readable !== data.stream) {
 | 
			
		||||
		data.stream.on('error', commonReadableHandlerGenerator(req, res));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (req.query.download !== undefined) {
 | 
			
		||||
		res.header('Content-Disposition', 'attachment');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	res.header('Content-Type', data.contentType);
 | 
			
		||||
 | 
			
		||||
	data.stream.pipe(res);
 | 
			
		||||
 | 
			
		||||
	data.stream.on('end', () => {
 | 
			
		||||
		res.end();
 | 
			
		||||
	});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function sendFileById(req: express.Request, res: express.Response): Promise<void> {
 | 
			
		||||
| 
						 | 
				
			
			@ -112,18 +134,9 @@ async function sendFileById(req: express.Request, res: express.Response): Promis
 | 
			
		|||
 | 
			
		||||
	const bucket = await getGridFSBucket();
 | 
			
		||||
 | 
			
		||||
	const buffer = await ((id): Promise<Buffer> => new Promise((resolve, reject) => {
 | 
			
		||||
		const chunks = [];
 | 
			
		||||
		const readableStream = bucket.openDownloadStream(id);
 | 
			
		||||
		readableStream.on('data', chunk => {
 | 
			
		||||
			chunks.push(chunk);
 | 
			
		||||
		});
 | 
			
		||||
		readableStream.on('end', () => {
 | 
			
		||||
			resolve(Buffer.concat(chunks));
 | 
			
		||||
		});
 | 
			
		||||
	}))(fileId);
 | 
			
		||||
	const readable = bucket.openDownloadStream(fileId);
 | 
			
		||||
 | 
			
		||||
	send(buffer, file.contentType, req, res);
 | 
			
		||||
	send(readable, file.contentType, req, res);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue