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 cors from 'cors';
 | 
				
			||||||
import * as mongodb from 'mongodb';
 | 
					import * as mongodb from 'mongodb';
 | 
				
			||||||
import * as gm from 'gm';
 | 
					import * as gm from 'gm';
 | 
				
			||||||
 | 
					import * as stream from 'stream';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import DriveFile, { getGridFSBucket } from '../api/models/drive-file';
 | 
					import DriveFile, { getGridFSBucket } from '../api/models/drive-file';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,60 +34,81 @@ app.get('/', (req, res) => {
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
app.get('/default-avatar.jpg', (req, res) => {
 | 
					app.get('/default-avatar.jpg', (req, res) => {
 | 
				
			||||||
	// TODO: 非同期にしたい。Promise対応してないんだろうか...
 | 
						const file = fs.createReadStream(`${__dirname}/assets/avatar.jpg`);
 | 
				
			||||||
	const file = fs.readFileSync(`${__dirname}/assets/avatar.jpg`);
 | 
					 | 
				
			||||||
	send(file, 'image/jpeg', req, res);
 | 
						send(file, 'image/jpeg', req, res);
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
app.get('/app-default.jpg', (req, res) => {
 | 
					app.get('/app-default.jpg', (req, res) => {
 | 
				
			||||||
	// TODO: 非同期にしたい。Promise対応してないんだろうか...
 | 
						const file = fs.createReadStream(`${__dirname}/assets/dummy.png`);
 | 
				
			||||||
	const file = fs.readFileSync(`${__dirname}/assets/dummy.png`);
 | 
					 | 
				
			||||||
	send(file, 'image/png', req, res);
 | 
						send(file, 'image/png', req, res);
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function raw(data: Buffer, type: string, download: boolean, res: express.Response): Promise<any> {
 | 
					interface ISend {
 | 
				
			||||||
	res.header('Content-Type', type);
 | 
						contentType: string;
 | 
				
			||||||
 | 
						stream: stream.Readable;
 | 
				
			||||||
	if (download) {
 | 
					 | 
				
			||||||
		res.header('Content-Disposition', 'attachment');
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	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)) {
 | 
								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) {
 | 
							if (resize) {
 | 
				
			||||||
			g = g.resize(resize, resize);
 | 
								g = g.resize(resize, resize);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	g
 | 
							const stream = g
 | 
				
			||||||
			.compress('jpeg')
 | 
								.compress('jpeg')
 | 
				
			||||||
			.quality(80)
 | 
								.quality(80)
 | 
				
			||||||
		.toBuffer('jpeg', (err, img) => {
 | 
								.stream();
 | 
				
			||||||
			if (err !== undefined && err !== null) {
 | 
					 | 
				
			||||||
				console.error(err);
 | 
					 | 
				
			||||||
				res.sendStatus(500);
 | 
					 | 
				
			||||||
				return;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			res.header('Content-Type', 'image/jpeg');
 | 
							return {
 | 
				
			||||||
			res.send(img);
 | 
								contentType: 'image/jpeg',
 | 
				
			||||||
		});
 | 
								stream
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function send(data: Buffer, type: string, req: express.Request, res: express.Response): void {
 | 
					const commonReadableHandlerGenerator = (req: express.Request, res: express.Response) => (e: Error): void => {
 | 
				
			||||||
 | 
						console.dir(e);
 | 
				
			||||||
 | 
						req.destroy();
 | 
				
			||||||
 | 
						res.destroy(e);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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) {
 | 
							if (req.query.thumbnail !== undefined) {
 | 
				
			||||||
		thumbnail(data, type, req.query.size, res);
 | 
								return thumbnail(readable, type, req.query.size);
 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		raw(data, type, req.query.download !== undefined, res);
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							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> {
 | 
					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 bucket = await getGridFSBucket();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const buffer = await ((id): Promise<Buffer> => new Promise((resolve, reject) => {
 | 
						const readable = bucket.openDownloadStream(fileId);
 | 
				
			||||||
		const chunks = [];
 | 
					 | 
				
			||||||
		const readableStream = bucket.openDownloadStream(id);
 | 
					 | 
				
			||||||
		readableStream.on('data', chunk => {
 | 
					 | 
				
			||||||
			chunks.push(chunk);
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
		readableStream.on('end', () => {
 | 
					 | 
				
			||||||
			resolve(Buffer.concat(chunks));
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
	}))(fileId);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	send(buffer, file.contentType, req, res);
 | 
						send(readable, file.contentType, req, res);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue