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 0000000000..959afe86d5
Binary files /dev/null and b/apps/server/src/services/import/samples/utf8-filename.zip differ
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);