diff --git a/packages/nc-gui/components/cell/attachment/utils.ts b/packages/nc-gui/components/cell/attachment/utils.ts index 0e53d235c4..d77969db66 100644 --- a/packages/nc-gui/components/cell/attachment/utils.ts +++ b/packages/nc-gui/components/cell/attachment/utils.ts @@ -1,4 +1,5 @@ import type { AttachmentReqType, AttachmentType } from 'nocodb-sdk' +import { populateUniqueFileName } from 'nocodb-sdk' import DOMPurify from 'isomorphic-dompurify' import RenameFile from './RenameFile.vue' import { @@ -161,7 +162,7 @@ export const [useProvideAttachmentCell, useAttachmentCell] = useInjectionState( } else { const fileName = populateUniqueFileName( (file as AttachmentReqType).fileName ?? '', - [...attachments.value, ...imageUrls], + [...attachments.value, ...imageUrls].map((fn) => fn?.title || fn?.fileName), (file as File)?.type || (file as AttachmentReqType)?.mimetype || '', ) @@ -225,7 +226,7 @@ export const [useProvideAttachmentCell, useAttachmentCell] = useInjectionState( ...uploadedFile, title: populateUniqueFileName( uploadedFile?.title, - [...attachments.value, ...newAttachments], + [...attachments.value, ...newAttachments].map((fn) => fn?.title || fn?.fileName), uploadedFile?.mimetype, ), }) diff --git a/packages/nc-gui/composables/useMultiSelect/convertCellData.ts b/packages/nc-gui/composables/useMultiSelect/convertCellData.ts index 5ca19962a0..1ca5e60c32 100644 --- a/packages/nc-gui/composables/useMultiSelect/convertCellData.ts +++ b/packages/nc-gui/composables/useMultiSelect/convertCellData.ts @@ -1,6 +1,6 @@ import dayjs from 'dayjs' import type { AttachmentType, ColumnType, LinkToAnotherRecordType, SelectOptionsType } from 'nocodb-sdk' -import { UITypes } from 'nocodb-sdk' +import { UITypes, populateUniqueFileName } from 'nocodb-sdk' import type { AppInfo } from '~/composables/useGlobal' import { isBt, isMm, parseProp } from '#imports' @@ -203,7 +203,11 @@ export default function convertCellData( for (const att of attachments) { newAttachments.push({ ...att, - title: populateUniqueFileName(att?.title, [...oldAttachments, ...newAttachments], att?.mimetype), + title: populateUniqueFileName( + att?.title, + [...oldAttachments, ...newAttachments].map((fn) => fn?.title || fn?.fileName), + att?.mimetype, + ), }) } return JSON.stringify([...oldAttachments, ...newAttachments]) diff --git a/packages/nc-gui/composables/useMultiSelect/index.ts b/packages/nc-gui/composables/useMultiSelect/index.ts index 3cedc82563..c81ec5f702 100644 --- a/packages/nc-gui/composables/useMultiSelect/index.ts +++ b/packages/nc-gui/composables/useMultiSelect/index.ts @@ -11,7 +11,15 @@ import type { UserFieldRecordType, ViewType, } from 'nocodb-sdk' -import { UITypes, dateFormats, isDateMonthFormat, isSystemColumn, isVirtualCol, timeFormats } from 'nocodb-sdk' +import { + UITypes, + dateFormats, + isDateMonthFormat, + isSystemColumn, + isVirtualCol, + timeFormats, + populateUniqueFileName, +} from 'nocodb-sdk' import { parse } from 'papaparse' import type { Cell } from './cellRange' import { CellRange } from './cellRange' @@ -1253,7 +1261,7 @@ export function useMultiSelect( ...uploadedFile, title: populateUniqueFileName( uploadedFile?.title, - [...handleParseAttachmentCellData(oldValue), ...newAttachments], + [...handleParseAttachmentCellData(oldValue), ...newAttachments].map((fn) => fn?.title || fn?.fileName), uploadedFile?.mimetype, ), }) diff --git a/packages/nc-gui/utils/fileUtils.ts b/packages/nc-gui/utils/fileUtils.ts index c7a6a1a913..521bbf6e7c 100644 --- a/packages/nc-gui/utils/fileUtils.ts +++ b/packages/nc-gui/utils/fileUtils.ts @@ -77,18 +77,3 @@ export function extractImageSrcFromRawHtml(rawText: string) { return imgElement.getAttribute('src') } } - -export function populateUniqueFileName(fn: string, attachments: any[], mimeType: string) { - if (!mimeType) return fn - - // If the image extension is not present, the while loop will go into an infinite loop. So, add the extension first if not present. - if (!fn?.endsWith(mimeType.split('/')[1])) { - fn = `${fn}.${mimeType.split('/')[1]}` - } - - let c = 1 - while (attachments.some((att) => att?.title === fn || att?.fileName === fn)) { - fn = fn.replace(/(.+?)(\.[^.]+)$/, `$1(${c++})$2`) - } - return fn -} diff --git a/packages/nocodb-sdk/src/lib/helperFunctions.ts b/packages/nocodb-sdk/src/lib/helperFunctions.ts index 527e479af6..43a8d878ec 100644 --- a/packages/nocodb-sdk/src/lib/helperFunctions.ts +++ b/packages/nocodb-sdk/src/lib/helperFunctions.ts @@ -97,6 +97,36 @@ const getAvailableRollupForUiType = (type: string) => { } }; +function populateUniqueFileName( + fileName: string, + attachments: string[], + mimeType: string +) { + if (!mimeType) return fileName; + + // If the file extension is not present, the while loop will go into an infinite loop. So, add the extension first if not present. + if (!fileName?.endsWith(`.${mimeType.split('/')[1]}`)) { + fileName = `${fileName}.${mimeType.split('/')[1]}`; + } else if ( + fileName?.endsWith(`.${mimeType.split('/')[1]}`) && + fileName.length === `.${mimeType.split('/')[1]}`.length + ) { + fileName = `image.${mimeType.split('/')[1]}`; + } + + const match = fileName.match(/^(.+?)(\((\d+)\))?(\.[^.]+)$/); + + if (!match) return fileName; + + let c = !isNaN(parseInt(match[3])) ? parseInt(match[3]) : 1; + + while (attachments.some((fn) => fn === fileName)) { + fileName = `${match[1]}(${c++})${match[4]}`; + } + + return fileName; +} + export { filterOutSystemColumns, getSystemColumnsIds, @@ -106,4 +136,5 @@ export { extractRolesObj, stringifyRolesObj, getAvailableRollupForUiType, + populateUniqueFileName, }; diff --git a/packages/nocodb/src/services/public-datas.service.ts b/packages/nocodb/src/services/public-datas.service.ts index 675a072616..338e4a879c 100644 --- a/packages/nocodb/src/services/public-datas.service.ts +++ b/packages/nocodb/src/services/public-datas.service.ts @@ -1,7 +1,12 @@ import path from 'path'; import { Injectable } from '@nestjs/common'; import { nanoid } from 'nanoid'; -import { ErrorMessages, UITypes, ViewTypes } from 'nocodb-sdk'; +import { + ErrorMessages, + UITypes, + ViewTypes, + populateUniqueFileName, +} from 'nocodb-sdk'; import slash from 'slash'; import { nocoExecute } from 'nc-help'; @@ -396,16 +401,11 @@ export class PublicDatasService { attachments[fieldName] = attachments[fieldName] || []; let originalName = utf8ify(file.originalname); - let c = 1; - while ( - path.extname(originalName) && - attachments[fieldName].some((att) => att?.title === originalName) - ) { - originalName = originalName.replace( - /(.+?)(\.[^.]+)$/, - `$1(${c++})$2`, - ); - } + originalName = populateUniqueFileName( + originalName, + attachments[fieldName].map((att) => att?.title), + file.mimetype, + ); const fileName = `${nanoid(18)}${path.extname(originalName)}`;