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 { extension as getExtensionFromMime } from 'mime-types'; | ||||||
| import type { Ref } from 'vue'; | import type { Ref } from 'vue'; | ||||||
|  | import _ from 'lodash'; | ||||||
| 
 | 
 | ||||||
| function getFileExtensionFromBase64({ | export { getMimeTypeFromBase64, useDownloadFileFromBase64 }; | ||||||
|   base64String, | 
 | ||||||
|  | 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', |   defaultExtension = 'txt', | ||||||
| }: { | }: { | ||||||
|   base64String: string |   mimeType: string | undefined | ||||||
|   defaultExtension?: string |   defaultExtension?: string | ||||||
| }) { | }) { | ||||||
|   const hasMimeType = base64String.match(/data:(.*?);base64/i); |   if (mimeType) { | ||||||
| 
 |     return getExtensionFromMime(mimeType) ?? defaultExtension; | ||||||
|   if (hasMimeType) { |  | ||||||
|     return getExtensionFromMime(hasMimeType[1]) || defaultExtension; |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   return defaultExtension; |   return defaultExtension; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function useDownloadFileFromBase64({ source, filename }: { source: Ref<string>; filename?: string }) { | function useDownloadFileFromBase64({ source, filename }: { source: Ref<string>; filename?: string }) { | ||||||
|   return { |   return { | ||||||
|     download() { |     download() { | ||||||
|       const base64String = source.value; |       if (source.value === '') { | ||||||
| 
 |  | ||||||
|       if (base64String === '') { |  | ||||||
|         throw new Error('Base64 string is empty'); |         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'); |       const a = document.createElement('a'); | ||||||
|       a.href = base64String; |       a.href = base64String; | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user