@@ -33,7 +33,7 @@ import { AttachmentsStorageService } from './attachments-storage.service';
3333import StorageAdapter from './plugins/adapter' ;
3434import type { LocalStorage } from './plugins/local' ;
3535import { InjectStorageAdapter } from './plugins/storage' ;
36- import { getExtensionPreview } from './utils' ;
36+ import { getExtensionPreview , getSafeUploadContentType } from './utils' ;
3737@Injectable ( )
3838export class AttachmentsService {
3939 private logger = new Logger ( AttachmentsService . name ) ;
@@ -281,55 +281,6 @@ export class AttachmentsService {
281281 return await this . notifyToAttachmentItem ( token , filename ) ;
282282 }
283283
284- async uploadFromLocalFile ( filePath : string , filename : string ) : Promise < IAttachmentItem > {
285- const MAX_FILE_SIZE = this . thresholdConfig . maxOpenapiAttachmentUploadSize ;
286- const stat = await fs . promises . stat ( filePath ) ;
287- const contentLength = stat . size ;
288-
289- if ( contentLength > MAX_FILE_SIZE ) {
290- const maxSize = ( MAX_FILE_SIZE / ( 1024 * 1024 ) ) . toFixed ( 2 ) ;
291- throw new CustomHttpException (
292- `File size exceeds the maximum limit of ${ maxSize } MB` ,
293- HttpErrorCode . VALIDATION_ERROR ,
294- {
295- localization : {
296- i18nKey : 'httpErrors.attachment.fileSizeExceedsMaximumLimit' ,
297- context : {
298- maxSize : `${ maxSize } MB` ,
299- } ,
300- } ,
301- }
302- ) ;
303- }
304-
305- const contentType = mimeTypes . lookup ( filename ) || 'application/octet-stream' ;
306- const { token, url } = await this . signature ( {
307- type : UploadType . Table ,
308- contentLength,
309- contentType,
310- internal : true ,
311- } ) ;
312-
313- try {
314- await this . uploadStreamToStorage (
315- url ,
316- fs . createReadStream ( filePath ) ,
317- contentType ,
318- contentLength
319- ) ;
320- return await this . notifyToAttachmentItem ( token , filename ) ;
321- } finally {
322- await fs . promises . unlink ( filePath ) ;
323- // Clean up temp subdirectory (created by email attachment saver)
324- const dir = dirname ( filePath ) ;
325- try {
326- await fs . promises . rmdir ( dir ) ;
327- } catch {
328- /* directory not empty or already removed */
329- }
330- }
331- }
332-
333284 async uploadFromUrl (
334285 fileUrl : string ,
335286 uploadType : UploadType = UploadType . Table
@@ -394,7 +345,10 @@ export class AttachmentsService {
394345 const headResponse = await axios . head ( fileUrl ) ;
395346 contentLength =
396347 headResponse . headers [ 'content-length' ] && parseInt ( headResponse . headers [ 'content-length' ] ) ;
397- contentType = mimeTypes . lookup ( fileUrl ) || headResponse . headers [ 'content-type' ] ;
348+ contentType =
349+ mimeTypes . lookup ( fileUrl ) ||
350+ headResponse . headers [ 'content-type' ] ||
351+ 'application/octet-stream' ;
398352 this . logger . log (
399353 `HEAD request successful. Content-Length: ${ contentLength } , Content-Type: ${ contentType } `
400354 ) ;
@@ -460,7 +414,7 @@ export class AttachmentsService {
460414 try {
461415 await axios . put ( url , stream , {
462416 headers : {
463- 'Content-Type' : contentType ,
417+ 'Content-Type' : getSafeUploadContentType ( contentType ) ,
464418 'Content-Length' : contentLength ,
465419 } ,
466420 } ) ;
@@ -473,7 +427,12 @@ export class AttachmentsService {
473427 private getFilenameFromUrl ( url : string ) : string {
474428 const urlParts = new URL ( url ) ;
475429 const pathParts = urlParts . pathname . split ( '/' ) ;
476- return pathParts [ pathParts . length - 1 ] || 'downloaded_file' ;
430+ const rawFilename = pathParts [ pathParts . length - 1 ] || 'downloaded_file' ;
431+ try {
432+ return decodeURIComponent ( rawFilename ) ;
433+ } catch {
434+ return rawFilename ;
435+ }
477436 }
478437
479438 private async downloadFile (
0 commit comments