diff --git a/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.spec.ts b/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.spec.ts index bd3a0984574..1685ca1e202 100644 --- a/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.spec.ts +++ b/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.spec.ts @@ -380,18 +380,18 @@ describe("VaultExportService", () => { expect(attachment).toBeDefined(); }); - it("If a ciphers attachments filename is already present in the zipfile then append underscore and attachmentId for the new filename", async () => { + it("For each cipher attachment create a dedicated folder with the attachmentId as the name to put the attachment in", async () => { const cipherData = new CipherData(); cipherData.id = "mock-id"; const cipherView = new CipherView(new Cipher(cipherData)); // Create 1st attachment with the same filename for the cipher const attachmentView = new AttachmentView(new Attachment(new AttachmentData())); attachmentView.id = "id-of-file-1"; - attachmentView.fileName = "mock-file-name"; + attachmentView.fileName = "mock-file-name.txt"; // Create 2nd attachment with the same filename for the cipher const attachmentView2 = new AttachmentView(new Attachment(new AttachmentData())); attachmentView2.id = "id-of-file-2"; - attachmentView2.fileName = "mock-file-name"; + attachmentView2.fileName = "mock-file-name.txt"; cipherView.attachments = [attachmentView, attachmentView2]; cipherService.getAllDecrypted.mockResolvedValue([cipherView]); folderService.getAllDecryptedFromState.mockResolvedValue([]); @@ -409,10 +409,12 @@ describe("VaultExportService", () => { expect(exportedVault.type).toBe("application/zip"); const exportZip = exportedVault as ExportedVaultAsBlob; const zip = await JSZip.loadAsync(exportZip.data); - const attachment = await zip.file("attachments/mock-id/mock-file-name")?.async("blob"); + const attachment = await zip + .file("attachments/mock-id/id-of-file-1/mock-file-name.txt") + ?.async("blob"); expect(attachment).toBeDefined(); const attachment2 = await zip - .file("attachments/mock-id/mock-file-name_id-of-file-2") + .file("attachments/mock-id/id-of-file-2/mock-file-name.txt") ?.async("blob"); expect(attachment2).toBeDefined(); }); diff --git a/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.ts b/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.ts index 072b13a318c..5a894e93eef 100644 --- a/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.ts +++ b/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.ts @@ -118,12 +118,10 @@ export class IndividualVaultExportService const response = await this.downloadAttachment(cipher.id, attachment.id); const decBuf = await this.decryptAttachment(cipher, attachment, response); - // To prevent files with the same name from overwriting each other, we append the attachment id to the file name, - // if a file with the same name already exists in the folder - if (cipherFolder.file(attachment.fileName) != null) { - attachment.fileName += `_${attachment.id}`; - } - cipherFolder.file(attachment.fileName, decBuf); + // To prevent files with the same name from overwriting each other, a folder with the attachmentId is created and then the attachment is saved within it + // This also has the benefit of retaining the attachmentId, which is useful when restoring backups + const attachmentIdFolder = cipherFolder.folder(attachment.id); + attachmentIdFolder.file(attachment.fileName, decBuf); } }