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>
This commit is contained in:
		
							parent
							
								
									ca43a25569
								
							
						
					
					
						commit
						043e4f0a08
					
				
							
								
								
									
										32
									
								
								src/composable/downloadBase64.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/composable/downloadBase64.test.ts
									
									
									
									
									
										Normal file
									
								
							| @ -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 }); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| @ -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; | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user