perf(server): improvement of external mediaProxy (#9787)

* perf(server): improvement of external mediaProxy

* add a comment

* ✌️

* /filesでsharpの処理を行わずリダイレクトする

* fix

* thumbnail => static

* Fix #9788

* add avatar mode

* add url

* fix

* static.webp

* remove encodeURIComponent from media proxy path

* remove existance check
This commit is contained in:
tamaina 2023-02-04 13:38:51 +09:00 committed by GitHub
parent 0c12e80106
commit 2dfed75402
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 110 additions and 62 deletions

View file

@ -137,38 +137,38 @@ export class FileServerService {
try {
if (file.state === 'remote') {
const convertFile = async () => {
if (file.fileRole === 'thumbnail') {
if (['image/jpeg', 'image/webp', 'image/avif', 'image/png', 'image/svg+xml'].includes(file.mime)) {
return this.imageProcessingService.convertToWebpStream(
file.path,
498,
280
);
} else if (file.mime.startsWith('video/')) {
return await this.videoProcessingService.generateVideoThumbnail(file.path);
}
}
let image: IImageStreamable | null = null;
if (file.fileRole === 'webpublic') {
if (['image/svg+xml'].includes(file.mime)) {
return this.imageProcessingService.convertToWebpStream(
file.path,
2048,
2048,
{ ...webpDefault, lossless: true }
)
}
}
if (file.fileRole === 'thumbnail') {
if (isMimeImage(file.mime, 'sharp-convertible-image')) {
reply.header('Cache-Control', 'max-age=31536000, immutable');
return {
const url = new URL(`${this.config.mediaProxy}/static.webp`);
url.searchParams.set('url', file.url);
url.searchParams.set('static', '1');
return await reply.redirect(301, url.toString());
} else if (file.mime.startsWith('video/')) {
image = await this.videoProcessingService.generateVideoThumbnail(file.path);
}
}
if (file.fileRole === 'webpublic') {
if (['image/svg+xml'].includes(file.mime)) {
reply.header('Cache-Control', 'max-age=31536000, immutable');
const url = new URL(`${this.config.mediaProxy}/svg.webp`);
url.searchParams.set('url', file.url);
return await reply.redirect(301, url.toString());
}
}
if (!image) {
image = {
data: fs.createReadStream(file.path),
ext: file.ext,
type: file.mime,
};
};
const image = await convertFile();
}
if ('pipe' in image.data && typeof image.data.pipe === 'function') {
// image.dataがstreamなら、stream終了後にcleanup
@ -180,7 +180,6 @@ export class FileServerService {
}
reply.header('Content-Type', FILE_TYPE_BROWSERSAFE.includes(image.type) ? image.type : 'application/octet-stream');
reply.header('Cache-Control', 'max-age=31536000, immutable');
return image.data;
}
@ -217,6 +216,23 @@ export class FileServerService {
return;
}
if (this.config.externalMediaProxyEnabled) {
// 外部のメディアプロキシが有効なら、そちらにリダイレクト
reply.header('Cache-Control', 'public, max-age=259200'); // 3 days
const url = new URL(`${this.config.mediaProxy}/${request.params.url || ''}`);
for (const [key, value] of Object.entries(request.query)) {
url.searchParams.append(key, value);
}
return await reply.redirect(
301,
url.toString(),
);
}
// Create temp file
const file = await this.getStreamAndTypeFromUrl(url);
if (file === '404') {
@ -236,7 +252,7 @@ export class FileServerService {
const isAnimationConvertibleImage = isMimeImage(file.mime, 'sharp-animation-convertible-image');
let image: IImageStreamable | null = null;
if ('emoji' in request.query && isConvertibleImage) {
if (('emoji' in request.query || 'avatar' in request.query) && isConvertibleImage) {
if (!isAnimationConvertibleImage && !('static' in request.query)) {
image = {
data: fs.createReadStream(file.path),
@ -246,7 +262,7 @@ export class FileServerService {
} else {
const data = sharp(file.path, { animated: !('static' in request.query) })
.resize({
height: 128,
height: 'emoji' in request.query ? 128 : 320,
withoutEnlargement: true,
})
.webp(webpDefault);
@ -370,7 +386,7 @@ export class FileServerService {
@bindThis
private async getFileFromKey(key: string): Promise<
{ state: 'remote'; fileRole: 'thumbnail' | 'webpublic' | 'original'; file: DriveFile; mime: string; ext: string | null; path: string; cleanup: () => void; }
{ state: 'remote'; fileRole: 'thumbnail' | 'webpublic' | 'original'; file: DriveFile; url: string; mime: string; ext: string | null; path: string; cleanup: () => void; }
| { state: 'stored_internal'; fileRole: 'thumbnail' | 'webpublic' | 'original'; file: DriveFile; mime: string; ext: string | null; path: string; }
| '404'
| '204'
@ -392,6 +408,7 @@ export class FileServerService {
const result = await this.downloadAndDetectTypeFromUrl(file.uri);
return {
...result,
url: file.uri,
fileRole: isThumbnail ? 'thumbnail' : isWebpublic ? 'webpublic' : 'original',
file,
}