summaryrefslogtreecommitdiff
path: root/packages/backend/src/server/FileServerService.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/backend/src/server/FileServerService.ts')
-rw-r--r--packages/backend/src/server/FileServerService.ts145
1 files changed, 120 insertions, 25 deletions
diff --git a/packages/backend/src/server/FileServerService.ts b/packages/backend/src/server/FileServerService.ts
index e82ef64dc4..6d24898acc 100644
--- a/packages/backend/src/server/FileServerService.ts
+++ b/packages/backend/src/server/FileServerService.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
@@ -9,7 +9,7 @@ import { dirname } from 'node:path';
import { Inject, Injectable } from '@nestjs/common';
import rename from 'rename';
import sharp from 'sharp';
-import { sharpBmp } from 'sharp-read-bmp';
+import { sharpBmp } from '@misskey-dev/sharp-read-bmp';
import type { Config } from '@/config.js';
import type { MiDriveFile, DriveFilesRepository } from '@/models/_.js';
import { DI } from '@/di-symbols.js';
@@ -27,6 +27,7 @@ import { LoggerService } from '@/core/LoggerService.js';
import { bindThis } from '@/decorators.js';
import { isMimeImage } from '@/misc/is-mime-image.js';
import { correctFilename } from '@/misc/correct-filename.js';
+import { handleRequestRedirectToOmitSearch } from '@/misc/fastify-hook-handlers.js';
import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions } from 'fastify';
const _filename = fileURLToPath(import.meta.url);
@@ -65,20 +66,23 @@ export class FileServerService {
done();
});
- fastify.get('/files/app-default.jpg', (request, reply) => {
- const file = fs.createReadStream(`${_dirname}/assets/dummy.png`);
- reply.header('Content-Type', 'image/jpeg');
- reply.header('Cache-Control', 'max-age=31536000, immutable');
- return reply.send(file);
- });
+ fastify.register((fastify, options, done) => {
+ fastify.addHook('onRequest', handleRequestRedirectToOmitSearch);
+ fastify.get('/files/app-default.jpg', (request, reply) => {
+ const file = fs.createReadStream(`${_dirname}/assets/dummy.png`);
+ reply.header('Content-Type', 'image/jpeg');
+ reply.header('Cache-Control', 'max-age=31536000, immutable');
+ return reply.send(file);
+ });
- fastify.get<{ Params: { key: string; } }>('/files/:key', async (request, reply) => {
- return await this.sendDriveFile(request, reply)
- .catch(err => this.errorHandler(request, reply, err));
- });
- fastify.get<{ Params: { key: string; } }>('/files/:key/*', async (request, reply) => {
- return await this.sendDriveFile(request, reply)
- .catch(err => this.errorHandler(request, reply, err));
+ fastify.get<{ Params: { key: string; } }>('/files/:key', async (request, reply) => {
+ return await this.sendDriveFile(request, reply)
+ .catch(err => this.errorHandler(request, reply, err));
+ });
+ fastify.get<{ Params: { key: string; } }>('/files/:key/*', async (request, reply) => {
+ return await reply.redirect(301, `${this.config.url}/files/${request.params.key}`);
+ });
+ done();
});
fastify.get<{
@@ -166,11 +170,35 @@ export class FileServerService {
}
if (!image) {
- image = {
- data: fs.createReadStream(file.path),
- ext: file.ext,
- type: file.mime,
- };
+ if (request.headers.range && file.file.size > 0) {
+ const range = request.headers.range as string;
+ const parts = range.replace(/bytes=/, '').split('-');
+ const start = parseInt(parts[0], 10);
+ let end = parts[1] ? parseInt(parts[1], 10) : file.file.size - 1;
+ if (end > file.file.size) {
+ end = file.file.size - 1;
+ }
+ const chunksize = end - start + 1;
+
+ image = {
+ data: fs.createReadStream(file.path, {
+ start,
+ end,
+ }),
+ ext: file.ext,
+ type: file.mime,
+ };
+
+ reply.header('Content-Range', `bytes ${start}-${end}/${file.file.size}`);
+ reply.header('Accept-Ranges', 'bytes');
+ reply.header('Content-Length', chunksize);
+ } else {
+ image = {
+ data: fs.createReadStream(file.path),
+ ext: file.ext,
+ type: file.mime,
+ };
+ }
}
if ('pipe' in image.data && typeof image.data.pipe === 'function') {
@@ -201,11 +229,54 @@ export class FileServerService {
reply.header('Content-Type', FILE_TYPE_BROWSERSAFE.includes(file.mime) ? file.mime : 'application/octet-stream');
reply.header('Cache-Control', 'max-age=31536000, immutable');
reply.header('Content-Disposition', contentDisposition('inline', filename));
+
+ if (request.headers.range && file.file.size > 0) {
+ const range = request.headers.range as string;
+ const parts = range.replace(/bytes=/, '').split('-');
+ const start = parseInt(parts[0], 10);
+ let end = parts[1] ? parseInt(parts[1], 10) : file.file.size - 1;
+ if (end > file.file.size) {
+ end = file.file.size - 1;
+ }
+ const chunksize = end - start + 1;
+ const fileStream = fs.createReadStream(file.path, {
+ start,
+ end,
+ });
+ reply.header('Content-Range', `bytes ${start}-${end}/${file.file.size}`);
+ reply.header('Accept-Ranges', 'bytes');
+ reply.header('Content-Length', chunksize);
+ reply.code(206);
+ return fileStream;
+ }
+
return fs.createReadStream(file.path);
} else {
reply.header('Content-Type', FILE_TYPE_BROWSERSAFE.includes(file.file.type) ? file.file.type : 'application/octet-stream');
reply.header('Cache-Control', 'max-age=31536000, immutable');
reply.header('Content-Disposition', contentDisposition('inline', file.filename));
+
+ if (request.headers.range && file.file.size > 0) {
+ const range = request.headers.range as string;
+ const parts = range.replace(/bytes=/, '').split('-');
+ const start = parseInt(parts[0], 10);
+ let end = parts[1] ? parseInt(parts[1], 10) : file.file.size - 1;
+ console.log(end);
+ if (end > file.file.size) {
+ end = file.file.size - 1;
+ }
+ const chunksize = end - start + 1;
+ const fileStream = fs.createReadStream(file.path, {
+ start,
+ end,
+ });
+ reply.header('Content-Range', `bytes ${start}-${end}/${file.file.size}`);
+ reply.header('Accept-Ranges', 'bytes');
+ reply.header('Content-Length', chunksize);
+ reply.code(206);
+ return fileStream;
+ }
+
return fs.createReadStream(file.path);
}
} catch (e) {
@@ -338,11 +409,35 @@ export class FileServerService {
}
if (!image) {
- image = {
- data: fs.createReadStream(file.path),
- ext: file.ext,
- type: file.mime,
- };
+ if (request.headers.range && file.file && file.file.size > 0) {
+ const range = request.headers.range as string;
+ const parts = range.replace(/bytes=/, '').split('-');
+ const start = parseInt(parts[0], 10);
+ let end = parts[1] ? parseInt(parts[1], 10) : file.file.size - 1;
+ if (end > file.file.size) {
+ end = file.file.size - 1;
+ }
+ const chunksize = end - start + 1;
+
+ image = {
+ data: fs.createReadStream(file.path, {
+ start,
+ end,
+ }),
+ ext: file.ext,
+ type: file.mime,
+ };
+
+ reply.header('Content-Range', `bytes ${start}-${end}/${file.file.size}`);
+ reply.header('Accept-Ranges', 'bytes');
+ reply.header('Content-Length', chunksize);
+ } else {
+ image = {
+ data: fs.createReadStream(file.path),
+ ext: file.ext,
+ type: file.mime,
+ };
+ }
}
if ('cleanup' in file) {