Content-Disposition in ObjectStrage (#4524)

* Content-Disposition in ObjectStrage

* encode filename
This commit is contained in:
MeiMei 2019-03-18 15:23:45 +09:00 committed by syuilo
parent 10527a8a76
commit bd1f2ba732
4 changed files with 23 additions and 10 deletions

View File

@ -108,6 +108,7 @@
"chai-http": "4.2.1", "chai-http": "4.2.1",
"chalk": "2.4.2", "chalk": "2.4.2",
"commander": "2.19.0", "commander": "2.19.0",
"content-disposition": "0.5.3",
"crc-32": "1.2.0", "crc-32": "1.2.0",
"css-loader": "2.1.1", "css-loader": "2.1.1",
"cssnano": "4.1.10", "cssnano": "4.1.10",

View File

@ -0,0 +1,6 @@
const cd = require('content-disposition');
export function contentDisposition(type: 'inline' | 'attachment', filename: string): string {
const fallback = filename.replace(/[^\w.-]/g, '_');
return cd(filename, { type, fallback });
}

View File

@ -6,6 +6,7 @@ import DriveFile, { getDriveFileBucket } from '../../models/drive-file';
import DriveFileThumbnail, { getDriveFileThumbnailBucket } from '../../models/drive-file-thumbnail'; import DriveFileThumbnail, { getDriveFileThumbnailBucket } from '../../models/drive-file-thumbnail';
import DriveFileWebpublic, { getDriveFileWebpublicBucket } from '../../models/drive-file-webpublic'; import DriveFileWebpublic, { getDriveFileWebpublicBucket } from '../../models/drive-file-webpublic';
import { serverLogger } from '..'; import { serverLogger } from '..';
import { contentDisposition } from '../../misc/content-disposition';
const assets = `${__dirname}/../../server/file/assets/`; const assets = `${__dirname}/../../server/file/assets/`;
@ -63,12 +64,12 @@ export default async function(ctx: Koa.BaseContext) {
if (thumb != null) { if (thumb != null) {
ctx.set('Content-Type', 'image/jpeg'); ctx.set('Content-Type', 'image/jpeg');
ctx.set('Content-Disposition', `filename="${rename(file.filename, { suffix: '-thumb', extname: '.jpeg' })}"`); ctx.set('Content-Disposition', contentDisposition('inline', `${rename(file.filename, { suffix: '-thumb', extname: '.jpeg' })}"`));
const bucket = await getDriveFileThumbnailBucket(); const bucket = await getDriveFileThumbnailBucket();
ctx.body = bucket.openDownloadStream(thumb._id); ctx.body = bucket.openDownloadStream(thumb._id);
} else { } else {
if (file.contentType.startsWith('image/')) { if (file.contentType.startsWith('image/')) {
ctx.set('Content-Disposition', `filename="${file.filename}"`); ctx.set('Content-Disposition', contentDisposition('inline', `${file.filename}"`));
await sendRaw(); await sendRaw();
} else { } else {
ctx.status = 404; ctx.status = 404;
@ -82,17 +83,17 @@ export default async function(ctx: Koa.BaseContext) {
if (web != null) { if (web != null) {
ctx.set('Content-Type', file.contentType); ctx.set('Content-Type', file.contentType);
ctx.set('Content-Disposition', `filename="${rename(file.filename, { suffix: '-web' })}"`); ctx.set('Content-Disposition', contentDisposition('inline', `${rename(file.filename, { suffix: '-web' })}"`));
const bucket = await getDriveFileWebpublicBucket(); const bucket = await getDriveFileWebpublicBucket();
ctx.body = bucket.openDownloadStream(web._id); ctx.body = bucket.openDownloadStream(web._id);
} else { } else {
ctx.set('Content-Disposition', `filename="${file.filename}"`); ctx.set('Content-Disposition', contentDisposition('inline', `${file.filename}"`));
await sendRaw(); await sendRaw();
} }
} else { } else {
if ('download' in ctx.query) { if ('download' in ctx.query) {
ctx.set('Content-Disposition', `attachment; filename="${file.filename}`); ctx.set('Content-Disposition', contentDisposition('attachment', `${file.filename}`));
} }
await sendRaw(); await sendRaw();

View File

@ -26,6 +26,7 @@ import { driveLogger } from './logger';
import { IImage, ConvertToJpeg, ConvertToWebp, ConvertToPng } from './image-processor'; import { IImage, ConvertToJpeg, ConvertToWebp, ConvertToPng } from './image-processor';
import Instance from '../../models/instance'; import Instance from '../../models/instance';
import checkSvg from '../../misc/check-svg'; import checkSvg from '../../misc/check-svg';
import { contentDisposition } from '../../misc/content-disposition';
const logger = driveLogger.createSubLogger('register', 'yellow'); const logger = driveLogger.createSubLogger('register', 'yellow');
@ -69,7 +70,7 @@ async function save(path: string, name: string, type: string, hash: string, size
//#region Uploads //#region Uploads
logger.info(`uploading original: ${key}`); logger.info(`uploading original: ${key}`);
const uploads = [ const uploads = [
upload(key, fs.createReadStream(path), type) upload(key, fs.createReadStream(path), type, name)
]; ];
if (alts.webpublic) { if (alts.webpublic) {
@ -77,7 +78,7 @@ async function save(path: string, name: string, type: string, hash: string, size
webpublicUrl = `${ baseUrl }/${ webpublicKey }`; webpublicUrl = `${ baseUrl }/${ webpublicKey }`;
logger.info(`uploading webpublic: ${webpublicKey}`); logger.info(`uploading webpublic: ${webpublicKey}`);
uploads.push(upload(webpublicKey, alts.webpublic.data, alts.webpublic.type)); uploads.push(upload(webpublicKey, alts.webpublic.data, alts.webpublic.type, name));
} }
if (alts.thumbnail) { if (alts.thumbnail) {
@ -198,13 +199,17 @@ export async function generateAlts(path: string, type: string, generateWeb: bool
/** /**
* Upload to ObjectStorage * Upload to ObjectStorage
*/ */
async function upload(key: string, stream: fs.ReadStream | Buffer, type: string) { async function upload(key: string, stream: fs.ReadStream | Buffer, type: string, filename?: string) {
const minio = new Minio.Client(config.drive.config); const minio = new Minio.Client(config.drive.config);
await minio.putObject(config.drive.bucket, key, stream, null, { const metadata = {
'Content-Type': type, 'Content-Type': type,
'Cache-Control': 'max-age=31536000, immutable' 'Cache-Control': 'max-age=31536000, immutable'
}); } as Minio.ItemBucketMetadata;
if (filename) metadata['Content-Disposition'] = contentDisposition('inline', filename);
await minio.putObject(config.drive.bucket, key, stream, null, metadata);
} }
/** /**