Sniff image MIME for ZK viewer blobs so save-as gets a real extension.

This commit is contained in:
2026-11-01 15:32:25 +01:00
parent 402e9df255
commit fb482a1052
5 changed files with 182 additions and 1 deletions
+4 -1
View File
@@ -3,6 +3,7 @@ import { ref } from 'vue';
import { api } from '@/plugins/axios';
import type { GetZkUploadCiphertextResult } from '@/types/upload/GetZkUploadCiphertextResult';
import { base64ToBytes } from '@/utils/encoding/base64ToBytes';
import { detectImageMimeType } from '@/utils/file/detectImageMimeType';
import { toArrayBuffer } from '@/utils/toArrayBuffer';
import { decryptPasswordProtectedZkUpload } from '@/utils/upload/decryptPasswordProtectedZkUpload';
import { decryptZkUpload } from '@/utils/upload/decryptZkUpload';
@@ -67,7 +68,9 @@ export const useZkViewStore = defineStore('zkView', () => {
throw new Error('Could not build image blob');
}
imageObjectUrl.value = URL.createObjectURL(new Blob([buffer]));
const mimeType = await detectImageMimeType(plaintext);
imageObjectUrl.value = URL.createObjectURL(new Blob([buffer], { type: mimeType }));
};
return {
@@ -0,0 +1,30 @@
import { describe, expect, it } from 'vitest';
import { detectImageMimeType } from '@/utils/file/detectImageMimeType';
const padToMinDetectLength = (header: number[]): Uint8Array => {
const out = new Uint8Array(4100);
out.set(header);
return out;
};
describe('detectImageMimeType', () => {
it('detects PNG', async () => {
const png = padToMinDetectLength([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
await expect(detectImageMimeType(png)).resolves.toBe('image/png');
});
it('detects JPEG', async () => {
const jpeg = padToMinDetectLength([0xff, 0xd8, 0xff, 0xe0]);
await expect(detectImageMimeType(jpeg)).resolves.toBe('image/jpeg');
});
it('falls back for unknown bytes', async () => {
await expect(detectImageMimeType(new Uint8Array(4100))).resolves.toBe(
'application/octet-stream'
);
});
});
@@ -0,0 +1,7 @@
import imageType from 'image-type';
export const detectImageMimeType = async (data: Uint8Array): Promise<string> => {
const result = await imageType(data);
return result?.mime ?? 'application/octet-stream';
};