mirror of
https://github.com/immich-app/immich.git
synced 2024-12-27 10:58:13 +02:00
fix(server): download album error handling (#917)
This commit is contained in:
parent
32e79ce7b3
commit
db0a55cd65
@ -25,7 +25,7 @@ import { GetAlbumsDto } from './dto/get-albums.dto';
|
|||||||
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
|
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
|
||||||
import { AlbumResponseDto } from './response-dto/album-response.dto';
|
import { AlbumResponseDto } from './response-dto/album-response.dto';
|
||||||
import { AlbumCountResponseDto } from './response-dto/album-count-response.dto';
|
import { AlbumCountResponseDto } from './response-dto/album-count-response.dto';
|
||||||
import {AddAssetsResponseDto} from "./response-dto/add-assets-response.dto";
|
import { AddAssetsResponseDto } from './response-dto/add-assets-response.dto';
|
||||||
import { Response as Res } from 'express';
|
import { Response as Res } from 'express';
|
||||||
|
|
||||||
// TODO might be worth creating a AlbumParamsDto that validates `albumId` instead of using the pipe.
|
// TODO might be worth creating a AlbumParamsDto that validates `albumId` instead of using the pipe.
|
||||||
@ -60,7 +60,7 @@ export class AlbumController {
|
|||||||
@GetAuthUser() authUser: AuthUserDto,
|
@GetAuthUser() authUser: AuthUserDto,
|
||||||
@Body(ValidationPipe) addAssetsDto: AddAssetsDto,
|
@Body(ValidationPipe) addAssetsDto: AddAssetsDto,
|
||||||
@Param('albumId', new ParseUUIDPipe({ version: '4' })) albumId: string,
|
@Param('albumId', new ParseUUIDPipe({ version: '4' })) albumId: string,
|
||||||
) : Promise<AddAssetsResponseDto> {
|
): Promise<AddAssetsResponseDto> {
|
||||||
return this.albumService.addAssetsToAlbum(authUser, addAssetsDto, albumId);
|
return this.albumService.addAssetsToAlbum(authUser, addAssetsDto, albumId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,6 +121,8 @@ export class AlbumController {
|
|||||||
@Param('albumId', new ParseUUIDPipe({ version: '4' })) albumId: string,
|
@Param('albumId', new ParseUUIDPipe({ version: '4' })) albumId: string,
|
||||||
@Response({ passthrough: true }) res: Res,
|
@Response({ passthrough: true }) res: Res,
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
return this.albumService.downloadArchive(authUser, albumId, res);
|
const { stream, filename } = await this.albumService.downloadArchive(authUser, albumId);
|
||||||
|
res.attachment(filename);
|
||||||
|
return stream;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import {
|
|||||||
ForbiddenException,
|
ForbiddenException,
|
||||||
Logger,
|
Logger,
|
||||||
InternalServerErrorException,
|
InternalServerErrorException,
|
||||||
|
StreamableFile,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { AuthUserDto } from '../../decorators/auth-user.decorator';
|
import { AuthUserDto } from '../../decorators/auth-user.decorator';
|
||||||
import { CreateAlbumDto } from './dto/create-album.dto';
|
import { CreateAlbumDto } from './dto/create-album.dto';
|
||||||
@ -20,8 +21,8 @@ import { AlbumCountResponseDto } from './response-dto/album-count-response.dto';
|
|||||||
import { ASSET_REPOSITORY, IAssetRepository } from '../asset/asset-repository';
|
import { ASSET_REPOSITORY, IAssetRepository } from '../asset/asset-repository';
|
||||||
import { AddAssetsResponseDto } from './response-dto/add-assets-response.dto';
|
import { AddAssetsResponseDto } from './response-dto/add-assets-response.dto';
|
||||||
import { AddAssetsDto } from './dto/add-assets.dto';
|
import { AddAssetsDto } from './dto/add-assets.dto';
|
||||||
import { Response as Res } from 'express';
|
|
||||||
import archiver from 'archiver';
|
import archiver from 'archiver';
|
||||||
|
import { extname } from 'path';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AlbumService {
|
export class AlbumService {
|
||||||
@ -149,17 +150,28 @@ export class AlbumService {
|
|||||||
return this._albumRepository.getCountByUserId(authUser.id);
|
return this._albumRepository.getCountByUserId(authUser.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
async downloadArchive(authUser: AuthUserDto, albumId: string, res: Res) {
|
async downloadArchive(authUser: AuthUserDto, albumId: string) {
|
||||||
try {
|
|
||||||
const album = await this._getAlbum({ authUser, albumId, validateIsOwner: false });
|
const album = await this._getAlbum({ authUser, albumId, validateIsOwner: false });
|
||||||
|
if (!album.assets || album.assets.length === 0) {
|
||||||
|
throw new BadRequestException('Cannot download an empty album.');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
const archive = archiver('zip', { store: true });
|
const archive = archiver('zip', { store: true });
|
||||||
res.attachment(`${album.albumName}.zip`);
|
const stream = new StreamableFile(archive);
|
||||||
archive.pipe(res);
|
|
||||||
album.assets?.forEach((a) => {
|
for (const { assetInfo } of album.assets) {
|
||||||
const name = `${a.assetInfo.exifInfo?.imageName || a.assetInfo.id}.${a.assetInfo.originalPath.split('.')[1]}`;
|
const { originalPath } = assetInfo;
|
||||||
archive.file(a.assetInfo.originalPath, { name });
|
const name = `${assetInfo.exifInfo?.imageName || assetInfo.id}${extname(originalPath)}`;
|
||||||
});
|
archive.file(originalPath, { name });
|
||||||
return archive.finalize();
|
}
|
||||||
|
|
||||||
|
archive.finalize();
|
||||||
|
|
||||||
|
return {
|
||||||
|
stream,
|
||||||
|
filename: `${album.albumName}.zip`,
|
||||||
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Logger.error(`Error downloading album ${e}`, 'downloadArchive');
|
Logger.error(`Error downloading album ${e}`, 'downloadArchive');
|
||||||
throw new InternalServerErrorException(`Failed to download album ${e}`, 'DownloadArchive');
|
throw new InternalServerErrorException(`Failed to download album ${e}`, 'DownloadArchive');
|
||||||
|
Loading…
Reference in New Issue
Block a user