mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-03 19:17:11 -05:00 
			
		
		
		
	hotfix(server): skip exif extraction on duplicate file (#590)
* fix(server): skip exif extraction on duplicate file * fix(server): typo * chore(server): remvoe un-use code
This commit is contained in:
		
							parent
							
								
									a467936e73
								
							
						
					
					
						commit
						7f6837c751
					
				@ -47,6 +47,7 @@ import { GetAssetThumbnailDto } from './dto/get-asset-thumbnail.dto';
 | 
			
		||||
import { AssetCountByTimeBucketResponseDto } from './response-dto/asset-count-by-time-group-response.dto';
 | 
			
		||||
import { GetAssetCountByTimeBucketDto } from './dto/get-asset-count-by-time-bucket.dto';
 | 
			
		||||
import { GetAssetByTimeBucketDto } from './dto/get-asset-by-time-bucket.dto';
 | 
			
		||||
import { QueryFailedError } from 'typeorm';
 | 
			
		||||
 | 
			
		||||
@UseGuards(JwtAuthGuard)
 | 
			
		||||
@ApiBearerAuth()
 | 
			
		||||
@ -74,8 +75,11 @@ export class AssetController {
 | 
			
		||||
    @UploadedFile() file: Express.Multer.File,
 | 
			
		||||
    @Body(ValidationPipe) assetInfo: CreateAssetDto,
 | 
			
		||||
  ): Promise<AssetFileUploadResponseDto> {
 | 
			
		||||
    const checksum = await this.assetService.calculateChecksum(file.path);
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      const savedAsset = await this.assetService.createUserAsset(authUser, assetInfo, file.path, file.mimetype);
 | 
			
		||||
      const savedAsset = await this.assetService.createUserAsset(authUser, assetInfo, file.path, file.mimetype, checksum);
 | 
			
		||||
 | 
			
		||||
      if (!savedAsset) {
 | 
			
		||||
        await this.backgroundTaskService.deleteFileOnDisk([
 | 
			
		||||
          {
 | 
			
		||||
@ -92,14 +96,20 @@ export class AssetController {
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      return new AssetFileUploadResponseDto(savedAsset.id);
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      Logger.error(`Error uploading file ${e}`);
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      await this.backgroundTaskService.deleteFileOnDisk([
 | 
			
		||||
        {
 | 
			
		||||
          originalPath: file.path,
 | 
			
		||||
        } as any,
 | 
			
		||||
      ]); // simulate asset to make use of delete queue (or use fs.unlink instead)
 | 
			
		||||
      throw new BadRequestException(`Error uploading file`, `${e}`);
 | 
			
		||||
 | 
			
		||||
      if (err instanceof QueryFailedError && (err as any).constraint === 'UQ_userid_checksum') {
 | 
			
		||||
        const existedAsset = await this.assetService.getAssetByChecksum(authUser.id, checksum)
 | 
			
		||||
        return new AssetFileUploadResponseDto(existedAsset.id);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      Logger.error(`Error uploading file ${err}`);
 | 
			
		||||
      throw new BadRequestException(`Error uploading file`, `${err}`);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -10,7 +10,7 @@ import {
 | 
			
		||||
} from '@nestjs/common';
 | 
			
		||||
import { InjectRepository } from '@nestjs/typeorm';
 | 
			
		||||
import { createHash } from 'node:crypto';
 | 
			
		||||
import { QueryFailedError, Repository } from 'typeorm';
 | 
			
		||||
import { Repository } from 'typeorm';
 | 
			
		||||
import { AuthUserDto } from '../../decorators/auth-user.decorator';
 | 
			
		||||
import { AssetEntity, AssetType } from '@app/database/entities/asset.entity';
 | 
			
		||||
import { constants, createReadStream, ReadStream, stat } from 'fs';
 | 
			
		||||
@ -53,10 +53,8 @@ export class AssetService {
 | 
			
		||||
    createAssetDto: CreateAssetDto,
 | 
			
		||||
    originalPath: string,
 | 
			
		||||
    mimeType: string,
 | 
			
		||||
    checksum: Buffer,
 | 
			
		||||
  ): Promise<AssetEntity> {
 | 
			
		||||
    const checksum = await this.calculateChecksum(originalPath);
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
    const assetEntity = await this._assetRepository.create(
 | 
			
		||||
      createAssetDto,
 | 
			
		||||
      authUser.id,
 | 
			
		||||
@ -66,18 +64,6 @@ export class AssetService {
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    return assetEntity;
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      if (err instanceof QueryFailedError && (err as any).constraint === 'UQ_userid_checksum') {
 | 
			
		||||
        const [assetEntity, _] = await Promise.all([
 | 
			
		||||
          this._assetRepository.getAssetByChecksum(authUser.id, checksum),
 | 
			
		||||
          fs.unlink(originalPath)
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        return assetEntity;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      throw err;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public async getUserAssetsByDeviceId(authUser: AuthUserDto, deviceId: string) {
 | 
			
		||||
@ -478,7 +464,11 @@ export class AssetService {
 | 
			
		||||
    return mapAssetCountByTimeBucket(result);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private calculateChecksum(filePath: string): Promise<Buffer> {
 | 
			
		||||
  getAssetByChecksum(userId: string, checksum: Buffer) {
 | 
			
		||||
    return this._assetRepository.getAssetByChecksum(userId, checksum);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  calculateChecksum(filePath: string): Promise<Buffer> {
 | 
			
		||||
    const fileReadStream = createReadStream(filePath);
 | 
			
		||||
    const sha1Hash = createHash('sha1');
 | 
			
		||||
    const deferred = new Promise<Buffer>((resolve, reject) => {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user