From d21557069c0a82f22682be5145b122e6261f040c Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 7 Apr 2026 17:01:34 +0300 Subject: [PATCH] fix(import/zip): ZIPs without language encoding flag importing garbage (closes #3013) --- .../src/services/import/samples/utf8-filename.zip | Bin 0 -> 184 bytes apps/server/src/services/import/zip.spec.ts | 5 +++++ apps/server/src/services/import/zip.ts | 9 ++++++++- 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 apps/server/src/services/import/samples/utf8-filename.zip diff --git a/apps/server/src/services/import/samples/utf8-filename.zip b/apps/server/src/services/import/samples/utf8-filename.zip new file mode 100644 index 0000000000000000000000000000000000000000..959afe86d5d8d4cd3845fb78b450920c14f66d81 GIT binary patch literal 184 zcmWIWW@h1H0D*ut?J*4kksAbnY!K#TkYRYXwfn{Tsd~97p&^_M%+J}cW_||Z(h6<{ zMwYLP3=AwFjmpXj5OrK!C8@#Hb0B=?{ PkU~Zv^as+xAPxfnRK6&9 literal 0 HcmV?d00001 diff --git a/apps/server/src/services/import/zip.spec.ts b/apps/server/src/services/import/zip.spec.ts index ecc070c6a9..21e4b19e14 100644 --- a/apps/server/src/services/import/zip.spec.ts +++ b/apps/server/src/services/import/zip.spec.ts @@ -71,6 +71,11 @@ describe("processNoteContent", () => { expect(content).toContain(` { + const { importedNote } = await testImport("utf8-filename.zip"); + expect(importedNote.title).toBe("测试"); + }); + it("can import old geomap notes", async () => { const { importedNote } = await testImport("geomap.zip"); expect(importedNote.type).toBe("book"); diff --git a/apps/server/src/services/import/zip.ts b/apps/server/src/services/import/zip.ts index 9c7cd8aefc..5d9e00d8ab 100644 --- a/apps/server/src/services/import/zip.ts +++ b/apps/server/src/services/import/zip.ts @@ -659,13 +659,20 @@ export function readContent(zipfile: yauzl.ZipFile, entry: yauzl.Entry): Promise export function readZipFile(buffer: Buffer, processEntryCallback: (zipfile: yauzl.ZipFile, entry: yauzl.Entry) => Promise) { return new Promise((res, rej) => { - yauzl.fromBuffer(buffer, { lazyEntries: true, validateEntrySizes: false }, (err, zipfile) => { + yauzl.fromBuffer(buffer, { lazyEntries: true, validateEntrySizes: false, decodeStrings: false }, (err, zipfile) => { if (err) rej(err); if (!zipfile) throw new Error("Unable to read zip file."); zipfile.readEntry(); zipfile.on("entry", async (entry) => { try { + // yauzl with decodeStrings: false returns fileName as a Buffer. + // We decode as UTF-8 to handle ZIP files that use UTF-8 filenames + // without setting the general purpose bit flag 11 (language encoding flag). + if (Buffer.isBuffer(entry.fileName)) { + entry.fileName = (entry.fileName as Buffer).toString("utf-8"); + } + await processEntryCallback(zipfile, entry); } catch (e) { rej(e);