fix(server): use storage template when downloading files

pull/24472/head
Jonathan Jogenfors 2025-12-09 01:50:17 +07:00
parent 287f6d5c94
commit 1a48ca3bdf
5 changed files with 13 additions and 14 deletions

@ -1,5 +1,6 @@
import { AssetMediaResponseDto, LoginResponseDto } from '@immich/sdk'; import { AssetMediaResponseDto, LoginResponseDto } from '@immich/sdk';
import { readFile, writeFile } from 'node:fs/promises'; import { readFile, writeFile } from 'node:fs/promises';
import path from 'node:path';
import { app, tempDir, utils } from 'src/utils'; import { app, tempDir, utils } from 'src/utils';
import request from 'supertest'; import request from 'supertest';
import { beforeAll, describe, expect, it } from 'vitest'; import { beforeAll, describe, expect, it } from 'vitest';
@ -43,13 +44,11 @@ describe('/download', () => {
await writeFile(`${tempDir}/archive.zip`, body); await writeFile(`${tempDir}/archive.zip`, body);
await utils.unzip(`${tempDir}/archive.zip`, `${tempDir}/archive`); await utils.unzip(`${tempDir}/archive.zip`, `${tempDir}/archive`);
const files = [ const files = [{ id: asset1.id }, { id: asset2.id }];
{ filename: 'example.png', id: asset1.id }, for (const { id } of files) {
{ filename: 'example+1.png', id: asset2.id },
];
for (const { id, filename } of files) {
const bytes = await readFile(`${tempDir}/archive/${filename}`);
const asset = await utils.getAssetInfo(admin.accessToken, id); const asset = await utils.getAssetInfo(admin.accessToken, id);
const bytes = await readFile(`${tempDir}/archive/${path.basename(asset.originalPath)}`);
expect(utils.sha1(bytes)).toBe(asset.checksum); expect(utils.sha1(bytes)).toBe(asset.checksum);
} }
}); });

@ -515,7 +515,7 @@ describe(AssetMediaService.name, () => {
await expect(sut.downloadOriginal(authStub.admin, 'asset-1')).resolves.toEqual( await expect(sut.downloadOriginal(authStub.admin, 'asset-1')).resolves.toEqual(
new ImmichFileResponse({ new ImmichFileResponse({
path: '/original/path.jpg', path: '/original/path.jpg',
fileName: 'asset-id.jpg', fileName: 'path.jpg',
contentType: 'image/jpeg', contentType: 'image/jpeg',
cacheControl: CacheControl.PrivateWithCache, cacheControl: CacheControl.PrivateWithCache,
}), }),

@ -1,5 +1,5 @@
import { BadRequestException, Injectable, InternalServerErrorException, NotFoundException } from '@nestjs/common'; import { BadRequestException, Injectable, InternalServerErrorException, NotFoundException } from '@nestjs/common';
import { extname } from 'node:path'; import { basename, extname } from 'node:path';
import sanitize from 'sanitize-filename'; import sanitize from 'sanitize-filename';
import { StorageCore } from 'src/cores/storage.core'; import { StorageCore } from 'src/cores/storage.core';
import { Asset } from 'src/database'; import { Asset } from 'src/database';
@ -200,7 +200,7 @@ export class AssetMediaService extends BaseService {
return new ImmichFileResponse({ return new ImmichFileResponse({
path: asset.originalPath, path: asset.originalPath,
fileName: asset.originalFileName, fileName: basename(asset.originalPath),
contentType: mimeTypes.lookup(asset.originalPath), contentType: mimeTypes.lookup(asset.originalPath),
cacheControl: CacheControl.PrivateWithCache, cacheControl: CacheControl.PrivateWithCache,
}); });

@ -165,7 +165,7 @@ describe(DownloadService.name, () => {
stream: archiveMock.stream, stream: archiveMock.stream,
}); });
expect(archiveMock.addFile).toHaveBeenCalledWith('/path/to/realpath.jpg', 'IMG_123.jpg'); expect(archiveMock.addFile).toHaveBeenCalledWith('/path/to/realpath.jpg', 'symlink.jpg');
}); });
}); });

@ -1,5 +1,5 @@
import { BadRequestException, Injectable } from '@nestjs/common'; import { BadRequestException, Injectable } from '@nestjs/common';
import { parse } from 'node:path'; import { basename, parse } from 'node:path';
import { StorageCore } from 'src/cores/storage.core'; import { StorageCore } from 'src/cores/storage.core';
import { AssetIdsDto } from 'src/dtos/asset.dto'; import { AssetIdsDto } from 'src/dtos/asset.dto';
import { AuthDto } from 'src/dtos/auth.dto'; import { AuthDto } from 'src/dtos/auth.dto';
@ -94,13 +94,13 @@ export class DownloadService extends BaseService {
continue; continue;
} }
const { originalPath, originalFileName } = asset; const { originalPath } = asset;
let filename = originalFileName; let filename = basename(originalPath);
const count = paths[filename] || 0; const count = paths[filename] || 0;
paths[filename] = count + 1; paths[filename] = count + 1;
if (count !== 0) { if (count !== 0) {
const parsedFilename = parse(originalFileName); const parsedFilename = parse(filename);
filename = `${parsedFilename.name}+${count}${parsedFilename.ext}`; filename = `${parsedFilename.name}+${count}${parsedFilename.ext}`;
} }