mirror of
https://github.com/immich-app/immich.git
synced 2025-03-01 15:11:21 +01:00
feature(server): compute sha1 during upload (#1424)
* feature(server): compute sha1 during upload * fix: clean up stream on error
This commit is contained in:
parent
7e53e33e0f
commit
c4e1bc35b4
3 changed files with 39 additions and 12 deletions
|
@ -19,7 +19,7 @@ import {
|
||||||
import { Authenticated } from '../../decorators/authenticated.decorator';
|
import { Authenticated } from '../../decorators/authenticated.decorator';
|
||||||
import { AssetService } from './asset.service';
|
import { AssetService } from './asset.service';
|
||||||
import { FileFieldsInterceptor } from '@nestjs/platform-express';
|
import { FileFieldsInterceptor } from '@nestjs/platform-express';
|
||||||
import { assetUploadOption } from '../../config/asset-upload.config';
|
import { assetUploadOption, ImmichFile } from '../../config/asset-upload.config';
|
||||||
import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator';
|
import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator';
|
||||||
import { ServeFileDto } from './dto/serve-file.dto';
|
import { ServeFileDto } from './dto/serve-file.dto';
|
||||||
import { Response as Res } from 'express';
|
import { Response as Res } from 'express';
|
||||||
|
@ -80,7 +80,7 @@ export class AssetController {
|
||||||
})
|
})
|
||||||
async uploadFile(
|
async uploadFile(
|
||||||
@GetAuthUser() authUser: AuthUserDto,
|
@GetAuthUser() authUser: AuthUserDto,
|
||||||
@UploadedFiles() files: { assetData: Express.Multer.File[]; livePhotoData?: Express.Multer.File[] },
|
@UploadedFiles() files: { assetData: ImmichFile[]; livePhotoData?: ImmichFile[] },
|
||||||
@Body(ValidationPipe) createAssetDto: CreateAssetDto,
|
@Body(ValidationPipe) createAssetDto: CreateAssetDto,
|
||||||
@Response({ passthrough: true }) res: Res,
|
@Response({ passthrough: true }) res: Res,
|
||||||
): Promise<AssetFileUploadResponseDto> {
|
): Promise<AssetFileUploadResponseDto> {
|
||||||
|
|
|
@ -55,6 +55,7 @@ import { CreateAssetsShareLinkDto } from './dto/create-asset-shared-link.dto';
|
||||||
import { mapSharedLink, SharedLinkResponseDto } from '@app/domain';
|
import { mapSharedLink, SharedLinkResponseDto } from '@app/domain';
|
||||||
import { UpdateAssetsToSharedLinkDto } from './dto/add-assets-to-shared-link.dto';
|
import { UpdateAssetsToSharedLinkDto } from './dto/add-assets-to-shared-link.dto';
|
||||||
import { AssetSearchDto } from './dto/asset-search.dto';
|
import { AssetSearchDto } from './dto/asset-search.dto';
|
||||||
|
import { ImmichFile } from '../../config/asset-upload.config';
|
||||||
|
|
||||||
const fileInfo = promisify(stat);
|
const fileInfo = promisify(stat);
|
||||||
|
|
||||||
|
@ -82,16 +83,16 @@ export class AssetService {
|
||||||
authUser: AuthUserDto,
|
authUser: AuthUserDto,
|
||||||
createAssetDto: CreateAssetDto,
|
createAssetDto: CreateAssetDto,
|
||||||
res: Res,
|
res: Res,
|
||||||
originalAssetData: Express.Multer.File,
|
originalAssetData: ImmichFile,
|
||||||
livePhotoAssetData?: Express.Multer.File,
|
livePhotoAssetData?: ImmichFile,
|
||||||
) {
|
) {
|
||||||
const checksum = await this.calculateChecksum(originalAssetData.path);
|
const checksum = originalAssetData.checksum;
|
||||||
const isLivePhoto = livePhotoAssetData !== undefined;
|
const isLivePhoto = livePhotoAssetData !== undefined;
|
||||||
let livePhotoAssetEntity: AssetEntity | undefined;
|
let livePhotoAssetEntity: AssetEntity | undefined;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (isLivePhoto) {
|
if (isLivePhoto) {
|
||||||
const livePhotoChecksum = await this.calculateChecksum(livePhotoAssetData.path);
|
const livePhotoChecksum = livePhotoAssetData.checksum;
|
||||||
livePhotoAssetEntity = await this.createUserAsset(
|
livePhotoAssetEntity = await this.createUserAsset(
|
||||||
authUser,
|
authUser,
|
||||||
createAssetDto,
|
createAssetDto,
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { APP_UPLOAD_LOCATION } from '@app/common/constants';
|
import { APP_UPLOAD_LOCATION } from '@app/common/constants';
|
||||||
import { BadRequestException, Logger, UnauthorizedException } from '@nestjs/common';
|
import { BadRequestException, Logger, UnauthorizedException } from '@nestjs/common';
|
||||||
import { MulterOptions } from '@nestjs/platform-express/multer/interfaces/multer-options.interface';
|
import { MulterOptions } from '@nestjs/platform-express/multer/interfaces/multer-options.interface';
|
||||||
import { randomUUID } from 'crypto';
|
import { createHash, randomUUID } from 'crypto';
|
||||||
import { Request } from 'express';
|
import { Request } from 'express';
|
||||||
import { existsSync, mkdirSync } from 'fs';
|
import { existsSync, mkdirSync } from 'fs';
|
||||||
import { diskStorage } from 'multer';
|
import { diskStorage, StorageEngine } from 'multer';
|
||||||
import { extname, join } from 'path';
|
import { extname, join } from 'path';
|
||||||
import sanitize from 'sanitize-filename';
|
import sanitize from 'sanitize-filename';
|
||||||
import { AuthUserDto } from '../decorators/auth-user.decorator';
|
import { AuthUserDto } from '../decorators/auth-user.decorator';
|
||||||
|
@ -12,14 +12,40 @@ import { patchFormData } from '../utils/path-form-data.util';
|
||||||
|
|
||||||
const logger = new Logger('AssetUploadConfig');
|
const logger = new Logger('AssetUploadConfig');
|
||||||
|
|
||||||
|
export interface ImmichFile extends Express.Multer.File {
|
||||||
|
/** sha1 hash of file */
|
||||||
|
checksum: Buffer;
|
||||||
|
}
|
||||||
|
|
||||||
export const assetUploadOption: MulterOptions = {
|
export const assetUploadOption: MulterOptions = {
|
||||||
fileFilter,
|
fileFilter,
|
||||||
storage: diskStorage({
|
storage: customStorage(),
|
||||||
destination,
|
|
||||||
filename,
|
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function customStorage(): StorageEngine {
|
||||||
|
const storage = diskStorage({ destination, filename });
|
||||||
|
|
||||||
|
return {
|
||||||
|
_handleFile(req, file, callback) {
|
||||||
|
const hash = createHash('sha1');
|
||||||
|
file.stream.on('data', (chunk) => hash.update(chunk));
|
||||||
|
|
||||||
|
storage._handleFile(req, file, (error, response) => {
|
||||||
|
if (error) {
|
||||||
|
hash.destroy();
|
||||||
|
callback(error);
|
||||||
|
} else {
|
||||||
|
callback(null, { ...response, checksum: hash.digest() } as ImmichFile);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_removeFile(req, file, callback) {
|
||||||
|
storage._removeFile(req, file, callback);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export const multerUtils = { fileFilter, filename, destination };
|
export const multerUtils = { fileFilter, filename, destination };
|
||||||
|
|
||||||
function fileFilter(req: Request, file: any, cb: any) {
|
function fileFilter(req: Request, file: any, cb: any) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue