mirror of
https://github.com/immich-app/immich.git
synced 2024-11-24 08:52:28 +02: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
@ -19,7 +19,7 @@ import {
|
||||
import { Authenticated } from '../../decorators/authenticated.decorator';
|
||||
import { AssetService } from './asset.service';
|
||||
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 { ServeFileDto } from './dto/serve-file.dto';
|
||||
import { Response as Res } from 'express';
|
||||
@ -80,7 +80,7 @@ export class AssetController {
|
||||
})
|
||||
async uploadFile(
|
||||
@GetAuthUser() authUser: AuthUserDto,
|
||||
@UploadedFiles() files: { assetData: Express.Multer.File[]; livePhotoData?: Express.Multer.File[] },
|
||||
@UploadedFiles() files: { assetData: ImmichFile[]; livePhotoData?: ImmichFile[] },
|
||||
@Body(ValidationPipe) createAssetDto: CreateAssetDto,
|
||||
@Response({ passthrough: true }) res: Res,
|
||||
): Promise<AssetFileUploadResponseDto> {
|
||||
|
@ -55,6 +55,7 @@ import { CreateAssetsShareLinkDto } from './dto/create-asset-shared-link.dto';
|
||||
import { mapSharedLink, SharedLinkResponseDto } from '@app/domain';
|
||||
import { UpdateAssetsToSharedLinkDto } from './dto/add-assets-to-shared-link.dto';
|
||||
import { AssetSearchDto } from './dto/asset-search.dto';
|
||||
import { ImmichFile } from '../../config/asset-upload.config';
|
||||
|
||||
const fileInfo = promisify(stat);
|
||||
|
||||
@ -82,16 +83,16 @@ export class AssetService {
|
||||
authUser: AuthUserDto,
|
||||
createAssetDto: CreateAssetDto,
|
||||
res: Res,
|
||||
originalAssetData: Express.Multer.File,
|
||||
livePhotoAssetData?: Express.Multer.File,
|
||||
originalAssetData: ImmichFile,
|
||||
livePhotoAssetData?: ImmichFile,
|
||||
) {
|
||||
const checksum = await this.calculateChecksum(originalAssetData.path);
|
||||
const checksum = originalAssetData.checksum;
|
||||
const isLivePhoto = livePhotoAssetData !== undefined;
|
||||
let livePhotoAssetEntity: AssetEntity | undefined;
|
||||
|
||||
try {
|
||||
if (isLivePhoto) {
|
||||
const livePhotoChecksum = await this.calculateChecksum(livePhotoAssetData.path);
|
||||
const livePhotoChecksum = livePhotoAssetData.checksum;
|
||||
livePhotoAssetEntity = await this.createUserAsset(
|
||||
authUser,
|
||||
createAssetDto,
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { APP_UPLOAD_LOCATION } from '@app/common/constants';
|
||||
import { BadRequestException, Logger, UnauthorizedException } from '@nestjs/common';
|
||||
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 { existsSync, mkdirSync } from 'fs';
|
||||
import { diskStorage } from 'multer';
|
||||
import { diskStorage, StorageEngine } from 'multer';
|
||||
import { extname, join } from 'path';
|
||||
import sanitize from 'sanitize-filename';
|
||||
import { AuthUserDto } from '../decorators/auth-user.decorator';
|
||||
@ -12,14 +12,40 @@ import { patchFormData } from '../utils/path-form-data.util';
|
||||
|
||||
const logger = new Logger('AssetUploadConfig');
|
||||
|
||||
export interface ImmichFile extends Express.Multer.File {
|
||||
/** sha1 hash of file */
|
||||
checksum: Buffer;
|
||||
}
|
||||
|
||||
export const assetUploadOption: MulterOptions = {
|
||||
fileFilter,
|
||||
storage: diskStorage({
|
||||
destination,
|
||||
filename,
|
||||
}),
|
||||
storage: customStorage(),
|
||||
};
|
||||
|
||||
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 };
|
||||
|
||||
function fileFilter(req: Request, file: any, cb: any) {
|
||||
|
Loading…
Reference in New Issue
Block a user