aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Corentin THOMASSET <corentin.thomasset74@gmail.com> 2023-11-13 11:44:23 +0100
committerGravatar GitHub <noreply@github.com> 2023-11-13 10:44:23 +0000
commit043e4f0a08fa9e1966b9733c58f33ca93d45da50 (patch)
tree64f205cd9e6f88202f77bad07dbf944d9acea80b
parentca43a25569d514e11a89b2ce2943c8918835b073 (diff)
downloadit-tools-043e4f0a08fa9e1966b9733c58f33ca93d45da50.tar.gz
it-tools-043e4f0a08fa9e1966b9733c58f33ca93d45da50.tar.zst
it-tools-043e4f0a08fa9e1966b9733c58f33ca93d45da50.zip
fix(base64-file-converter): fix downloading of index.html content without data preambula (#750)
* fix(base64-file-converter): fix downloading of index.html content without data preambula * feat(base64-file-converter): infer mime type from base64 signature --------- Co-authored-by: akharlov <harl_aa@skbkontur.ru>
-rw-r--r--src/composable/downloadBase64.test.ts32
-rw-r--r--src/composable/downloadBase64.ts52
2 files changed, 72 insertions, 12 deletions
diff --git a/src/composable/downloadBase64.test.ts b/src/composable/downloadBase64.test.ts
new file mode 100644
index 0000000..3f2e3f7
--- /dev/null
+++ b/src/composable/downloadBase64.test.ts
@@ -0,0 +1,32 @@
+import { describe, expect, it } from 'vitest';
+import { getMimeTypeFromBase64 } from './downloadBase64';
+
+describe('downloadBase64', () => {
+ describe('getMimeTypeFromBase64', () => {
+ it('when the base64 string has a data URI, it returns the mime type', () => {
+ expect(getMimeTypeFromBase64({ base64String: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA' })).to.deep.equal({ mimeType: 'image/png' });
+ expect(getMimeTypeFromBase64({ base64String: 'data:image/jpg;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA' })).to.deep.equal({ mimeType: 'image/jpg' });
+ });
+
+ it('when the base64 string has no data URI, it try to infer the mime type from the signature', () => {
+ // https://en.wikipedia.org/wiki/List_of_file_signatures
+
+ // PNG
+ expect(getMimeTypeFromBase64({ base64String: 'iVBORw0KGgoAAAANSUhEUgAAAAUA' })).to.deep.equal({ mimeType: 'image/png' });
+
+ // GIF
+ expect(getMimeTypeFromBase64({ base64String: 'R0lGODdh' })).to.deep.equal({ mimeType: 'image/gif' });
+ expect(getMimeTypeFromBase64({ base64String: 'R0lGODlh' })).to.deep.equal({ mimeType: 'image/gif' });
+
+ // JPG
+ expect(getMimeTypeFromBase64({ base64String: '/9j/' })).to.deep.equal({ mimeType: 'image/jpg' });
+
+ // PDF
+ expect(getMimeTypeFromBase64({ base64String: 'JVBERi0' })).to.deep.equal({ mimeType: 'application/pdf' });
+ });
+
+ it('when the base64 string has no data URI and no signature, it returns an undefined mimeType', () => {
+ expect(getMimeTypeFromBase64({ base64String: 'JVBERi' })).to.deep.equal({ mimeType: undefined });
+ });
+ });
+});
diff --git a/src/composable/downloadBase64.ts b/src/composable/downloadBase64.ts
index 3904315..37b0428 100644
--- a/src/composable/downloadBase64.ts
+++ b/src/composable/downloadBase64.ts
@@ -1,32 +1,60 @@
import { extension as getExtensionFromMime } from 'mime-types';
import type { Ref } from 'vue';
+import _ from 'lodash';
-function getFileExtensionFromBase64({
- base64String,
+export { getMimeTypeFromBase64, useDownloadFileFromBase64 };
+
+const commonMimeTypesSignatures = {
+ 'JVBERi0': 'application/pdf',
+ 'R0lGODdh': 'image/gif',
+ 'R0lGODlh': 'image/gif',
+ 'iVBORw0KGgo': 'image/png',
+ '/9j/': 'image/jpg',
+};
+
+function getMimeTypeFromBase64({ base64String }: { base64String: string }) {
+ const [,mimeTypeFromBase64] = base64String.match(/data:(.*?);base64/i) ?? [];
+
+ if (mimeTypeFromBase64) {
+ return { mimeType: mimeTypeFromBase64 };
+ }
+
+ const inferredMimeType = _.find(commonMimeTypesSignatures, (_mimeType, signature) => base64String.startsWith(signature));
+
+ if (inferredMimeType) {
+ return { mimeType: inferredMimeType };
+ }
+
+ return { mimeType: undefined };
+}
+
+function getFileExtensionFromMimeType({
+ mimeType,
defaultExtension = 'txt',
}: {
- base64String: string
+ mimeType: string | undefined
defaultExtension?: string
}) {
- const hasMimeType = base64String.match(/data:(.*?);base64/i);
-
- if (hasMimeType) {
- return getExtensionFromMime(hasMimeType[1]) || defaultExtension;
+ if (mimeType) {
+ return getExtensionFromMime(mimeType) ?? defaultExtension;
}
return defaultExtension;
}
-export function useDownloadFileFromBase64({ source, filename }: { source: Ref<string>; filename?: string }) {
+function useDownloadFileFromBase64({ source, filename }: { source: Ref<string>; filename?: string }) {
return {
download() {
- const base64String = source.value;
-
- if (base64String === '') {
+ if (source.value === '') {
throw new Error('Base64 string is empty');
}
- const cleanFileName = filename ?? `file.${getFileExtensionFromBase64({ base64String })}`;
+ const { mimeType } = getMimeTypeFromBase64({ base64String: source.value });
+ const base64String = mimeType
+ ? source.value
+ : `data:text/plain;base64,${source.value}`;
+
+ const cleanFileName = filename ?? `file.${getFileExtensionFromMimeType({ mimeType })}`;
const a = document.createElement('a');
a.href = base64String;