File hosting: inline file display

This commit is contained in:
Alice Gaudon 2020-08-11 19:02:42 +02:00
parent e92fd2ef4a
commit 010c716e14
2 changed files with 27 additions and 4 deletions

10
src/Utils.ts Normal file
View File

@ -0,0 +1,10 @@
export function encodeRFC5987ValueChars(str: string) {
return encodeURIComponent(str).
// Note that although RFC3986 reserves "!", RFC5987 does not,
// so we do not need to escape it
replace(/['()*]/g, c => "%" + c.charCodeAt(0).toString(16)). // i.e., %27 %28 %29 %2a (Note that valid encoding of "*" is %2A
// which necessitates calling toUpperCase() to properly encode)
// The following are not required for percent-encoding per RFC5987,
// so we can allow for a little better readability over the wire: |`^
replace(/%(7C|60|5E)/g, (str, hex) => String.fromCharCode(parseInt(hex, 16)));
}

View File

@ -1,6 +1,6 @@
import Controller from "wms-core/Controller"; import Controller from "wms-core/Controller";
import {NextFunction, Request, Response} from "express"; import {NextFunction, Request, Response} from "express";
import {BadRequestError, NotFoundHttpError, ServerError} from "wms-core/HttpError"; import {BadRequestError, ForbiddenHttpError, NotFoundHttpError, ServerError} from "wms-core/HttpError";
import config from "config"; import config from "config";
import {REQUIRE_REQUEST_AUTH_MIDDLEWARE} from "wms-core/auth/AuthComponent"; import {REQUIRE_REQUEST_AUTH_MIDDLEWARE} from "wms-core/auth/AuthComponent";
import URLRedirect from "../models/URLRedirect"; import URLRedirect from "../models/URLRedirect";
@ -8,12 +8,14 @@ import URLRedirectController from "./URLRedirectController";
import FileModel from "../models/FileModel"; import FileModel from "../models/FileModel";
import generateSlug from "../SlugGenerator"; import generateSlug from "../SlugGenerator";
import FileController, {FILE_UPLOAD_FORM_MIDDLEWARE} from "./FileController"; import FileController, {FILE_UPLOAD_FORM_MIDDLEWARE} from "./FileController";
import * as fs from "fs";
import {encodeRFC5987ValueChars} from "../Utils";
export default class LinkController extends Controller { export default class LinkController extends Controller {
routes(): void { routes(): void {
this.post('/', this.postFile, 'post-file', REQUIRE_REQUEST_AUTH_MIDDLEWARE, FILE_UPLOAD_FORM_MIDDLEWARE); this.post('/', this.postFile, 'post-file', REQUIRE_REQUEST_AUTH_MIDDLEWARE, FILE_UPLOAD_FORM_MIDDLEWARE);
this.delete('/:slug', FileController.deleteFileRoute, 'delete-file', REQUIRE_REQUEST_AUTH_MIDDLEWARE); this.delete('/:slug', FileController.deleteFileRoute, 'delete-file', REQUIRE_REQUEST_AUTH_MIDDLEWARE);
this.get('/:slug', this.downloadFile, 'get-file'); this.get('/:slug', this.getFile, 'get-file');
this.put('/:slug', this.putFile, 'put-file', REQUIRE_REQUEST_AUTH_MIDDLEWARE, FILE_UPLOAD_FORM_MIDDLEWARE); this.put('/:slug', this.putFile, 'put-file', REQUIRE_REQUEST_AUTH_MIDDLEWARE, FILE_UPLOAD_FORM_MIDDLEWARE);
this.post('/', URLRedirectController.addURL, 'post-url', REQUIRE_REQUEST_AUTH_MIDDLEWARE); this.post('/', URLRedirectController.addURL, 'post-url', REQUIRE_REQUEST_AUTH_MIDDLEWARE);
@ -24,7 +26,7 @@ export default class LinkController extends Controller {
this.get(/(.*)/, this.domainFilter); this.get(/(.*)/, this.domainFilter);
} }
protected async downloadFile(req: Request, res: Response, next: NextFunction): Promise<void> { protected async getFile(req: Request, res: Response, next: NextFunction): Promise<void> {
console.log('get file', req.params.slug) console.log('get file', req.params.slug)
const file = await FileModel.getBySlug(req.params.slug); const file = await FileModel.getBySlug(req.params.slug);
if (!file) return next(); if (!file) return next();
@ -33,9 +35,20 @@ export default class LinkController extends Controller {
return next(); return next();
} }
// File type
const fileName = file.real_name!;
const parts = fileName.split('.');
res.type(parts[parts.length - 1]);
// File name
res.header('Content-Disposition', `inline; filename*=UTF-8''${encodeRFC5987ValueChars(fileName)}`);
switch (file.storage_type) { switch (file.storage_type) {
case 'local': case 'local':
res.download(file.storage_path!, file.real_name!); fs.readFile(file.storage_path!, (err, data) => {
if (err) return next(err);
res.send(data);
});
break; break;
default: default:
throw new ServerError(`This file cannot be served. Download protocol for ${file.storage_type} storage type not implemented.`); throw new ServerError(`This file cannot be served. Download protocol for ${file.storage_type} storage type not implemented.`);