Skip to content

Commit 435d812

Browse files
committed
feat(base64 file converter): add a filename and extension fields
Add filename and extension (auto filled if data url) to allow downloading with right extension and filename Fix CorentinTh#788
1 parent 3e27051 commit 435d812

File tree

2 files changed

+84
-20
lines changed

2 files changed

+84
-20
lines changed

src/composable/downloadBase64.ts

Lines changed: 47 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1-
import { extension as getExtensionFromMime } from 'mime-types';
1+
import { extension as getExtensionFromMimeType, extension as getMimeTypeFromExtension } from 'mime-types';
22
import type { Ref } from 'vue';
33
import _ from 'lodash';
44

5-
export { getMimeTypeFromBase64, useDownloadFileFromBase64 };
5+
export {
6+
getMimeTypeFromBase64,
7+
getMimeTypeFromExtension, getExtensionFromMimeType,
8+
useDownloadFileFromBase64, useDownloadFileFromBase64Refs,
9+
};
610

711
const commonMimeTypesSignatures = {
812
'JVBERi0': 'application/pdf',
@@ -36,30 +40,55 @@ function getFileExtensionFromMimeType({
3640
defaultExtension?: string
3741
}) {
3842
if (mimeType) {
39-
return getExtensionFromMime(mimeType) ?? defaultExtension;
43+
return getExtensionFromMimeType(mimeType) ?? defaultExtension;
4044
}
4145

4246
return defaultExtension;
4347
}
4448

45-
function useDownloadFileFromBase64({ source, filename }: { source: Ref<string>; filename?: string }) {
46-
return {
47-
download() {
48-
if (source.value === '') {
49-
throw new Error('Base64 string is empty');
50-
}
49+
function downloadFromBase64({ sourceValue, filename, extension, fileMimeType }:
50+
{ sourceValue: string; filename?: string; extension?: string; fileMimeType?: string }) {
51+
if (sourceValue === '') {
52+
throw new Error('Base64 string is empty');
53+
}
54+
55+
const defaultExtension = extension ?? 'txt';
56+
const { mimeType } = getMimeTypeFromBase64({ base64String: sourceValue });
57+
let base64String = sourceValue;
58+
if (!mimeType) {
59+
const targetMimeType = fileMimeType ?? getMimeTypeFromExtension(defaultExtension);
60+
base64String = `data:${targetMimeType};base64,${sourceValue}`;
61+
}
62+
63+
const cleanExtension = extension ?? getFileExtensionFromMimeType(
64+
{ mimeType, defaultExtension });
65+
let cleanFileName = filename ?? `file.${cleanExtension}`;
66+
if (extension && !cleanFileName.endsWith(`.${extension}`)) {
67+
cleanFileName = `${cleanFileName}.${cleanExtension}`;
68+
}
5169

52-
const { mimeType } = getMimeTypeFromBase64({ base64String: source.value });
53-
const base64String = mimeType
54-
? source.value
55-
: `data:text/plain;base64,${source.value}`;
70+
const a = document.createElement('a');
71+
a.href = base64String;
72+
a.download = cleanFileName;
73+
a.click();
74+
}
5675

57-
const cleanFileName = filename ?? `file.${getFileExtensionFromMimeType({ mimeType })}`;
76+
function useDownloadFileFromBase64(
77+
{ source, filename, extension, fileMimeType }:
78+
{ source: Ref<string>; filename?: string; extension?: string; fileMimeType?: string }) {
79+
return {
80+
download() {
81+
downloadFromBase64({ sourceValue: source.value, filename, extension, fileMimeType });
82+
},
83+
};
84+
}
5885

59-
const a = document.createElement('a');
60-
a.href = base64String;
61-
a.download = cleanFileName;
62-
a.click();
86+
function useDownloadFileFromBase64Refs(
87+
{ source, filename, extension }:
88+
{ source: Ref<string>; filename?: Ref<string>; extension?: Ref<string> }) {
89+
return {
90+
download() {
91+
downloadFromBase64({ sourceValue: source.value, filename: filename?.value, extension: extension?.value });
6392
},
6493
};
6594
}

src/tools/base64-file-converter/base64-file-converter.vue

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,19 @@
22
import { useBase64 } from '@vueuse/core';
33
import type { Ref } from 'vue';
44
import { useCopy } from '@/composable/copy';
5-
import { useDownloadFileFromBase64 } from '@/composable/downloadBase64';
5+
import { getExtensionFromMimeType, getMimeTypeFromBase64, useDownloadFileFromBase64Refs } from '@/composable/downloadBase64';
66
import { useValidation } from '@/composable/validation';
77
import { isValidBase64 } from '@/utils/base64';
88
9+
const fileName = ref('file');
10+
const fileExtension = ref('');
911
const base64Input = ref('');
10-
const { download } = useDownloadFileFromBase64({ source: base64Input });
12+
const { download } = useDownloadFileFromBase64Refs(
13+
{
14+
source: base64Input,
15+
filename: fileName,
16+
extension: fileExtension,
17+
});
1118
const base64InputValidation = useValidation({
1219
source: base64Input,
1320
rules: [
@@ -18,6 +25,16 @@ const base64InputValidation = useValidation({
1825
],
1926
});
2027
28+
watch(
29+
base64Input,
30+
(newValue, _) => {
31+
const { mimeType } = getMimeTypeFromBase64({ base64String: newValue });
32+
if (mimeType) {
33+
fileExtension.value = getExtensionFromMimeType(mimeType) || fileExtension.value;
34+
}
35+
},
36+
);
37+
2138
function downloadFile() {
2239
if (!base64InputValidation.isValid) {
2340
return;
@@ -44,6 +61,24 @@ async function onUpload(file: File) {
4461

4562
<template>
4663
<c-card title="Base64 to file">
64+
<n-grid cols="3" x-gap="12">
65+
<n-gi span="2">
66+
<c-input-text
67+
v-model:value="fileName"
68+
label="File Name"
69+
placeholder="Download filename"
70+
mb-2
71+
/>
72+
</n-gi>
73+
<n-gi>
74+
<c-input-text
75+
v-model:value="fileExtension"
76+
label="Extension"
77+
placeholder="Extension"
78+
mb-2
79+
/>
80+
</n-gi>
81+
</n-grid>
4782
<c-input-text
4883
v-model:value="base64Input"
4984
multiline

0 commit comments

Comments
 (0)