feat(new tool): Hex File Converter
Convert File to/from Hex representation Fix #724
This commit is contained in:
		
							parent
							
								
									318fb6efb9
								
							
						
					
					
						commit
						73e97f97bb
					
				
							
								
								
									
										8
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -83,6 +83,7 @@ declare module '@vue/runtime-core' { | |||||||
|     GitMemo: typeof import('./src/tools/git-memo/git-memo.vue')['default'] |     GitMemo: typeof import('./src/tools/git-memo/git-memo.vue')['default'] | ||||||
|     'GitMemo.content': typeof import('./src/tools/git-memo/git-memo.content.md')['default'] |     'GitMemo.content': typeof import('./src/tools/git-memo/git-memo.content.md')['default'] | ||||||
|     HashText: typeof import('./src/tools/hash-text/hash-text.vue')['default'] |     HashText: typeof import('./src/tools/hash-text/hash-text.vue')['default'] | ||||||
|  |     HexFileConverter: typeof import('./src/tools/hex-file-converter/hex-file-converter.vue')['default'] | ||||||
|     HmacGenerator: typeof import('./src/tools/hmac-generator/hmac-generator.vue')['default'] |     HmacGenerator: typeof import('./src/tools/hmac-generator/hmac-generator.vue')['default'] | ||||||
|     'Home.page': typeof import('./src/pages/Home.page.vue')['default'] |     'Home.page': typeof import('./src/pages/Home.page.vue')['default'] | ||||||
|     HtmlEntities: typeof import('./src/tools/html-entities/html-entities.vue')['default'] |     HtmlEntities: typeof import('./src/tools/html-entities/html-entities.vue')['default'] | ||||||
| @ -132,19 +133,18 @@ declare module '@vue/runtime-core' { | |||||||
|     NCode: typeof import('naive-ui')['NCode'] |     NCode: typeof import('naive-ui')['NCode'] | ||||||
|     NCollapseTransition: typeof import('naive-ui')['NCollapseTransition'] |     NCollapseTransition: typeof import('naive-ui')['NCollapseTransition'] | ||||||
|     NConfigProvider: typeof import('naive-ui')['NConfigProvider'] |     NConfigProvider: typeof import('naive-ui')['NConfigProvider'] | ||||||
|  |     NDivider: typeof import('naive-ui')['NDivider'] | ||||||
|     NEllipsis: typeof import('naive-ui')['NEllipsis'] |     NEllipsis: typeof import('naive-ui')['NEllipsis'] | ||||||
|     NForm: typeof import('naive-ui')['NForm'] |  | ||||||
|     NFormItem: typeof import('naive-ui')['NFormItem'] |     NFormItem: typeof import('naive-ui')['NFormItem'] | ||||||
|  |     NGi: typeof import('naive-ui')['NGi'] | ||||||
|  |     NGrid: typeof import('naive-ui')['NGrid'] | ||||||
|     NH1: typeof import('naive-ui')['NH1'] |     NH1: typeof import('naive-ui')['NH1'] | ||||||
|     NH3: typeof import('naive-ui')['NH3'] |     NH3: typeof import('naive-ui')['NH3'] | ||||||
|     NIcon: typeof import('naive-ui')['NIcon'] |     NIcon: typeof import('naive-ui')['NIcon'] | ||||||
|     NInputNumber: typeof import('naive-ui')['NInputNumber'] |  | ||||||
|     NLayout: typeof import('naive-ui')['NLayout'] |     NLayout: typeof import('naive-ui')['NLayout'] | ||||||
|     NLayoutSider: typeof import('naive-ui')['NLayoutSider'] |     NLayoutSider: typeof import('naive-ui')['NLayoutSider'] | ||||||
|     NMenu: typeof import('naive-ui')['NMenu'] |     NMenu: typeof import('naive-ui')['NMenu'] | ||||||
|     NScrollbar: typeof import('naive-ui')['NScrollbar'] |     NScrollbar: typeof import('naive-ui')['NScrollbar'] | ||||||
|     NSlider: typeof import('naive-ui')['NSlider'] |  | ||||||
|     NSwitch: typeof import('naive-ui')['NSwitch'] |  | ||||||
|     NumeronymGenerator: typeof import('./src/tools/numeronym-generator/numeronym-generator.vue')['default'] |     NumeronymGenerator: typeof import('./src/tools/numeronym-generator/numeronym-generator.vue')['default'] | ||||||
|     OtpCodeGeneratorAndValidator: typeof import('./src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue')['default'] |     OtpCodeGeneratorAndValidator: typeof import('./src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue')['default'] | ||||||
|     PasswordStrengthAnalyser: typeof import('./src/tools/password-strength-analyser/password-strength-analyser.vue')['default'] |     PasswordStrengthAnalyser: typeof import('./src/tools/password-strength-analyser/password-strength-analyser.vue')['default'] | ||||||
|  | |||||||
| @ -134,6 +134,7 @@ | |||||||
|     "unplugin-icons": "^0.17.0", |     "unplugin-icons": "^0.17.0", | ||||||
|     "unplugin-vue-components": "^0.25.0", |     "unplugin-vue-components": "^0.25.0", | ||||||
|     "vite": "^4.4.9", |     "vite": "^4.4.9", | ||||||
|  |     "vite-plugin-node-polyfills": "^0.22.0", | ||||||
|     "vite-plugin-pwa": "^0.16.0", |     "vite-plugin-pwa": "^0.16.0", | ||||||
|     "vite-plugin-vue-markdown": "^0.23.5", |     "vite-plugin-vue-markdown": "^0.23.5", | ||||||
|     "vite-svg-loader": "^4.0.0", |     "vite-svg-loader": "^4.0.0", | ||||||
|  | |||||||
							
								
								
									
										640
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										640
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -1,6 +1,7 @@ | |||||||
| import { extension as getExtensionFromMimeType, extension as getMimeTypeFromExtension } from 'mime-types'; | import { extension as getExtensionFromMimeType, extension as getMimeTypeFromExtension } from 'mime-types'; | ||||||
| import type { Ref } from 'vue'; | import type { MaybeRef, Ref } from 'vue'; | ||||||
| import _ from 'lodash'; | import _ from 'lodash'; | ||||||
|  | import { get } from '@vueuse/core'; | ||||||
| 
 | 
 | ||||||
| export { | export { | ||||||
|   getMimeTypeFromBase64, |   getMimeTypeFromBase64, | ||||||
| @ -75,21 +76,11 @@ function downloadFromBase64({ sourceValue, filename, extension, fileMimeType }: | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function useDownloadFileFromBase64( | function useDownloadFileFromBase64( | ||||||
|   { source, filename, extension, fileMimeType }: |  | ||||||
|   { source: Ref<string>; filename?: string; extension?: string; fileMimeType?: string }) { |  | ||||||
|   return { |  | ||||||
|     download() { |  | ||||||
|       downloadFromBase64({ sourceValue: source.value, filename, extension, fileMimeType }); |  | ||||||
|     }, |  | ||||||
|   }; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| function useDownloadFileFromBase64Refs( |  | ||||||
|   { source, filename, extension }: |   { source, filename, extension }: | ||||||
|   { source: Ref<string>; filename?: Ref<string>; extension?: Ref<string> }) { |   { source: MaybeRef<string>; filename?: MaybeRef<string>; extension?: MaybeRef<string> }) { | ||||||
|   return { |   return { | ||||||
|     download() { |     download() { | ||||||
|       downloadFromBase64({ sourceValue: source.value, filename: filename?.value, extension: extension?.value }); |       downloadFromBase64({ sourceValue: get(source), filename: get(filename), extension: get(extension) }); | ||||||
|     }, |     }, | ||||||
|   }; |   }; | ||||||
| } | } | ||||||
| @ -116,3 +107,13 @@ function previewImageFromBase64(base64String: string): HTMLImageElement { | |||||||
| 
 | 
 | ||||||
|   return img; |   return img; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | function useDownloadFileFromBase64Refs( | ||||||
|  |   { source, filename, extension }: | ||||||
|  |   { source: Ref<string>; filename?: Ref<string>; extension?: Ref<string> }) { | ||||||
|  |   return { | ||||||
|  |     download() { | ||||||
|  |       downloadFromBase64({ sourceValue: source.value, filename: filename?.value, extension: extension?.value }); | ||||||
|  |     }, | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  | |||||||
							
								
								
									
										145
									
								
								src/tools/hex-file-converter/hex-file-converter.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								src/tools/hex-file-converter/hex-file-converter.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,145 @@ | |||||||
|  | <script setup lang="ts"> | ||||||
|  | import { Buffer } from 'node:buffer'; | ||||||
|  | import type { Ref } from 'vue'; | ||||||
|  | import { useCopy } from '@/composable/copy'; | ||||||
|  | import { useDownloadFileFromBase64 } from '@/composable/downloadBase64'; | ||||||
|  | 
 | ||||||
|  | const fileName = ref(''); | ||||||
|  | const fileExtension = ref(''); | ||||||
|  | const hexInput = ref(''); | ||||||
|  | const base64Input = computed(() => { | ||||||
|  |   const hexString = hexInput.value?.replace(/[^\da-f]/gi, ''); | ||||||
|  |   try { | ||||||
|  |     return `data:application/octet-stream;base64,${Buffer.from(hexString, 'hex').toString('base64')}`; | ||||||
|  |   } | ||||||
|  |   catch { | ||||||
|  |     return ''; | ||||||
|  |   } | ||||||
|  | }); | ||||||
|  | const { download } = useDownloadFileFromBase64( | ||||||
|  |   { | ||||||
|  |     source: base64Input, | ||||||
|  |     filename: fileName, | ||||||
|  |     extension: fileExtension, | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  | function downloadFile() { | ||||||
|  |   try { | ||||||
|  |     download(); | ||||||
|  |   } | ||||||
|  |   catch (_) { | ||||||
|  |     // | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function buf2hex(buffer: ArrayBuffer, separator: string): string { | ||||||
|  |   return [...new Uint8Array(buffer)] | ||||||
|  |     .map(x => x.toString(16).padStart(2, '0')) | ||||||
|  |     .join(separator); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | async function ReadFileAsHex(file: File, separator: string = ' '): Promise<string> { | ||||||
|  |   return new Promise<string>((resolve, reject) => { | ||||||
|  |     const reader = new FileReader(); | ||||||
|  |     reader.onload = () => { | ||||||
|  |       resolve(buf2hex(reader.result as ArrayBuffer, separator)); | ||||||
|  |     }; | ||||||
|  |     reader.onerror = () => reject(reader.error?.toString()); | ||||||
|  |     reader.readAsArrayBuffer(file); | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const separator = useStorage('hex-converter:sep', ' '); | ||||||
|  | const fileInput = ref() as Ref<File>; | ||||||
|  | const fileHex = computedAsync(async () => { | ||||||
|  |   const file = fileInput.value; | ||||||
|  |   const sep = separator.value; | ||||||
|  | 
 | ||||||
|  |   return await ReadFileAsHex(file, sep); | ||||||
|  | }); | ||||||
|  | const { copy: copyFileHex } = useCopy({ source: fileHex, text: 'Hex string copied to the clipboard' }); | ||||||
|  | 
 | ||||||
|  | function onUpload(file: File) { | ||||||
|  |   if (file) { | ||||||
|  |     fileInput.value = file; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <template> | ||||||
|  |   <c-card title="HEX to file"> | ||||||
|  |     <n-grid cols="3" x-gap="12"> | ||||||
|  |       <n-gi span="2"> | ||||||
|  |         <c-input-text | ||||||
|  |           v-model:value="fileName" | ||||||
|  |           label="File Name" | ||||||
|  |           placeholder="Download filename" | ||||||
|  |           mb-2 | ||||||
|  |         /> | ||||||
|  |       </n-gi> | ||||||
|  |       <n-gi> | ||||||
|  |         <c-input-text | ||||||
|  |           v-model:value="fileExtension" | ||||||
|  |           label="Extension" | ||||||
|  |           placeholder="Extension" | ||||||
|  |         /> | ||||||
|  |       </n-gi> | ||||||
|  |     </n-grid> | ||||||
|  | 
 | ||||||
|  |     <n-form-item label="Content in Hex"> | ||||||
|  |       <c-input-text | ||||||
|  |         v-model:value="hexInput" | ||||||
|  |         multiline | ||||||
|  |         placeholder="Put your Hex file string here..." | ||||||
|  |         rows="5" | ||||||
|  |       /> | ||||||
|  |     </n-form-item> | ||||||
|  | 
 | ||||||
|  |     <n-divider /> | ||||||
|  | 
 | ||||||
|  |     <div flex justify-center> | ||||||
|  |       <c-button :disabled="hexInput === ''" @click="downloadFile()"> | ||||||
|  |         Download file | ||||||
|  |       </c-button> | ||||||
|  |     </div> | ||||||
|  |   </c-card> | ||||||
|  | 
 | ||||||
|  |   <c-card title="File to HEX"> | ||||||
|  |     <c-file-upload | ||||||
|  |       title="Drag and drop a file here, or click to select a file" | ||||||
|  |       mb-2 | ||||||
|  |       @file-upload="onUpload" | ||||||
|  |     /> | ||||||
|  | 
 | ||||||
|  |     <c-input-text | ||||||
|  |       v-model:value="separator" | ||||||
|  |       label="Separator" | ||||||
|  |       label-position="left" | ||||||
|  |       placeholder="Separator" | ||||||
|  |       mb-2 | ||||||
|  |     /> | ||||||
|  | 
 | ||||||
|  |     <n-divider /> | ||||||
|  | 
 | ||||||
|  |     <n-form-item label="File in Hex"> | ||||||
|  |       <c-input-text | ||||||
|  |         :value="fileHex" | ||||||
|  |         multiline readonly | ||||||
|  |         placeholder="File in hex will be here" | ||||||
|  |         rows="5" mb-2 | ||||||
|  |       /> | ||||||
|  |     </n-form-item> | ||||||
|  | 
 | ||||||
|  |     <div flex justify-center> | ||||||
|  |       <c-button @click="copyFileHex()"> | ||||||
|  |         Copy | ||||||
|  |       </c-button> | ||||||
|  |     </div> | ||||||
|  |   </c-card> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <style lang="less" scoped> | ||||||
|  | ::v-deep(.n-upload-trigger) { | ||||||
|  |   width: 100%; | ||||||
|  | } | ||||||
|  | </style> | ||||||
							
								
								
									
										12
									
								
								src/tools/hex-file-converter/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/tools/hex-file-converter/index.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | import { FileDigit } from '@vicons/tabler'; | ||||||
|  | import { defineTool } from '../tool'; | ||||||
|  | 
 | ||||||
|  | export const tool = defineTool({ | ||||||
|  |   name: 'HEX File Converter', | ||||||
|  |   path: '/hex-file-converter', | ||||||
|  |   description: 'Convert between file and hexadecimal representation', | ||||||
|  |   keywords: ['hex', 'file', 'converter'], | ||||||
|  |   component: () => import('./hex-file-converter.vue'), | ||||||
|  |   icon: FileDigit, | ||||||
|  |   createdAt: new Date('2024-08-15'), | ||||||
|  | }); | ||||||
| @ -2,6 +2,7 @@ import { tool as base64FileConverter } from './base64-file-converter'; | |||||||
| import { tool as base64StringConverter } from './base64-string-converter'; | import { tool as base64StringConverter } from './base64-string-converter'; | ||||||
| import { tool as basicAuthGenerator } from './basic-auth-generator'; | import { tool as basicAuthGenerator } from './basic-auth-generator'; | ||||||
| import { tool as emailNormalizer } from './email-normalizer'; | import { tool as emailNormalizer } from './email-normalizer'; | ||||||
|  | import { tool as hexFileConverter } from './hex-file-converter'; | ||||||
| 
 | 
 | ||||||
| import { tool as asciiTextDrawer } from './ascii-text-drawer'; | import { tool as asciiTextDrawer } from './ascii-text-drawer'; | ||||||
| 
 | 
 | ||||||
| @ -98,6 +99,7 @@ export const toolsByCategory: ToolCategory[] = [ | |||||||
|       romanNumeralConverter, |       romanNumeralConverter, | ||||||
|       base64StringConverter, |       base64StringConverter, | ||||||
|       base64FileConverter, |       base64FileConverter, | ||||||
|  |       hexFileConverter, | ||||||
|       colorConverter, |       colorConverter, | ||||||
|       caseConverter, |       caseConverter, | ||||||
|       textToNatoAlphabet, |       textToNatoAlphabet, | ||||||
|  | |||||||
| @ -15,6 +15,7 @@ import { VitePWA } from 'vite-plugin-pwa'; | |||||||
| import markdown from 'vite-plugin-vue-markdown'; | import markdown from 'vite-plugin-vue-markdown'; | ||||||
| import svgLoader from 'vite-svg-loader'; | import svgLoader from 'vite-svg-loader'; | ||||||
| import { configDefaults } from 'vitest/config'; | import { configDefaults } from 'vitest/config'; | ||||||
|  | import { nodePolyfills } from 'vite-plugin-node-polyfills' | ||||||
| 
 | 
 | ||||||
| const baseUrl = process.env.BASE_URL ?? '/'; | const baseUrl = process.env.BASE_URL ?? '/'; | ||||||
| 
 | 
 | ||||||
| @ -97,6 +98,7 @@ export default defineConfig({ | |||||||
|       resolvers: [NaiveUiResolver(), IconsResolver({ prefix: 'icon' })], |       resolvers: [NaiveUiResolver(), IconsResolver({ prefix: 'icon' })], | ||||||
|     }), |     }), | ||||||
|     Unocss(), |     Unocss(), | ||||||
|  |     nodePolyfills(), | ||||||
|   ], |   ], | ||||||
|   base: baseUrl, |   base: baseUrl, | ||||||
|   resolve: { |   resolve: { | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user