-
Notifications
You must be signed in to change notification settings - Fork 7.1k
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
import { openWindow } from './window'; | ||
|
||
interface DownloadOptions<T = string> { | ||
fileName?: string; | ||
source: T; | ||
target?: string; | ||
} | ||
|
||
const DEFAULT_FILENAME = 'downloaded_file'; | ||
|
||
/** | ||
* 通过 URL 下载文件,支持跨域 | ||
* @throws {Error} - 当下载失败时抛出错误 | ||
*/ | ||
export async function downloadFileFromUrl({ | ||
fileName, | ||
source, | ||
target = '_blank', | ||
}: DownloadOptions): Promise<void> { | ||
if (!source || typeof source !== 'string') { | ||
throw new Error('Invalid URL.'); | ||
} | ||
|
||
const isChrome = window.navigator.userAgent.toLowerCase().includes('chrome'); | ||
const isSafari = window.navigator.userAgent.toLowerCase().includes('safari'); | ||
|
||
if (/iP/.test(window.navigator.userAgent)) { | ||
console.error('Your browser does not support download!'); | ||
return; | ||
} | ||
|
||
if (isChrome || isSafari) { | ||
triggerDownload(source, resolveFileName(source, fileName)); | ||
} | ||
if (!source.includes('?')) { | ||
source += '?download'; | ||
} | ||
|
||
openWindow(source, { target }); | ||
} | ||
|
||
/** | ||
* 通过 Base64 下载文件 | ||
*/ | ||
export function downloadFileFromBase64({ fileName, source }: DownloadOptions) { | ||
if (!source || typeof source !== 'string') { | ||
throw new Error('Invalid Base64 data.'); | ||
} | ||
|
||
const resolvedFileName = fileName || DEFAULT_FILENAME; | ||
triggerDownload(source, resolvedFileName); | ||
} | ||
|
||
/** | ||
* 通过图片 URL 下载图片文件 | ||
*/ | ||
export async function downloadFileFromImageUrl({ | ||
fileName, | ||
source, | ||
}: DownloadOptions) { | ||
const base64 = await urlToBase64(source); | ||
downloadFileFromBase64({ fileName, source: base64 }); | ||
} | ||
|
||
/** | ||
* 通过 Blob 下载文件 | ||
* @param blob - 文件的 Blob 对象 | ||
Check warning on line 67 in packages/@core/base/shared/src/utils/download.ts GitHub Actions / Lint (ubuntu-latest)
Check warning on line 67 in packages/@core/base/shared/src/utils/download.ts GitHub Actions / Lint (ubuntu-latest)
Check warning on line 67 in packages/@core/base/shared/src/utils/download.ts GitHub Actions / Lint (windows-latest)
Check warning on line 67 in packages/@core/base/shared/src/utils/download.ts GitHub Actions / Lint (windows-latest)
|
||
* @param fileName - 可选,下载的文件名称 | ||
*/ | ||
export function downloadFileFromBlob({ | ||
fileName = DEFAULT_FILENAME, | ||
source, | ||
}: DownloadOptions<Blob>): void { | ||
if (!(source instanceof Blob)) { | ||
throw new TypeError('Invalid Blob data.'); | ||
} | ||
|
||
const url = URL.createObjectURL(source); | ||
triggerDownload(url, fileName); | ||
} | ||
|
||
/** | ||
* 下载文件,支持 Blob、字符串和其他 BlobPart 类型 | ||
* @param data - 文件的 BlobPart 数据 | ||
Check warning on line 84 in packages/@core/base/shared/src/utils/download.ts GitHub Actions / Lint (ubuntu-latest)
Check warning on line 84 in packages/@core/base/shared/src/utils/download.ts GitHub Actions / Lint (ubuntu-latest)
Check warning on line 84 in packages/@core/base/shared/src/utils/download.ts GitHub Actions / Lint (windows-latest)
Check warning on line 84 in packages/@core/base/shared/src/utils/download.ts GitHub Actions / Lint (windows-latest)
|
||
* @param fileName - 下载的文件名称 | ||
*/ | ||
export function downloadFileFromBlobPart({ | ||
fileName = DEFAULT_FILENAME, | ||
source, | ||
}: DownloadOptions<BlobPart>): void { | ||
// 如果 data 不是 Blob,则转换为 Blob | ||
const blob = | ||
source instanceof Blob | ||
? source | ||
: new Blob([source], { type: 'application/octet-stream' }); | ||
|
||
// 创建对象 URL 并触发下载 | ||
const url = URL.createObjectURL(blob); | ||
triggerDownload(url, fileName); | ||
} | ||
|
||
/** | ||
* img url to base64 | ||
* @param url | ||
*/ | ||
export function urlToBase64(url: string, mineType?: string): Promise<string> { | ||
return new Promise((resolve, reject) => { | ||
let canvas = document.createElement('CANVAS') as HTMLCanvasElement | null; | ||
const ctx = canvas?.getContext('2d'); | ||
const img = new Image(); | ||
img.crossOrigin = ''; | ||
img.addEventListener('load', () => { | ||
if (!canvas || !ctx) { | ||
return reject(new Error('Failed to create canvas.')); | ||
} | ||
canvas.height = img.height; | ||
canvas.width = img.width; | ||
ctx.drawImage(img, 0, 0); | ||
const dataURL = canvas.toDataURL(mineType || 'image/png'); | ||
canvas = null; | ||
resolve(dataURL); | ||
}); | ||
img.src = url; | ||
}); | ||
} | ||
|
||
/** | ||
* 通用下载触发函数 | ||
* @param href - 文件下载的 URL | ||
* @param fileName - 下载文件的名称,如果未提供则自动识别 | ||
* @param revokeDelay - 清理 URL 的延迟时间 (毫秒) | ||
*/ | ||
export function triggerDownload( | ||
href: string, | ||
fileName: string | undefined, | ||
revokeDelay: number = 100, | ||
): void { | ||
const defaultFileName = 'downloaded_file'; | ||
const finalFileName = fileName || defaultFileName; | ||
|
||
const link = document.createElement('a'); | ||
link.href = href; | ||
link.download = finalFileName; | ||
link.style.display = 'none'; | ||
|
||
if (link.download === undefined) { | ||
link.setAttribute('target', '_blank'); | ||
} | ||
|
||
document.body.append(link); | ||
link.click(); | ||
link.remove(); | ||
|
||
// 清理临时 URL 以释放内存 | ||
setTimeout(() => URL.revokeObjectURL(href), revokeDelay); | ||
} | ||
|
||
function resolveFileName(url: string, fileName?: string): string { | ||
return fileName || url.slice(url.lastIndexOf('/') + 1) || DEFAULT_FILENAME; | ||
} |
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
<script setup lang="ts"> | ||
import { Page } from '@vben/common-ui'; | ||
import { | ||
downloadFileFromBase64, | ||
downloadFileFromBlobPart, | ||
downloadFileFromImageUrl, | ||
downloadFileFromUrl, | ||
} from '@vben/utils'; | ||
import { Button, Card } from 'ant-design-vue'; | ||
import imageBase64 from './base64'; | ||
</script> | ||
|
||
<template> | ||
<Page title="文件下载示例"> | ||
<Card title="根据文件地址下载文件"> | ||
<Button | ||
type="primary" | ||
@click=" | ||
downloadFileFromUrl({ | ||
source: | ||
'https://codeload.github.com/vbenjs/vue-vben-admin-doc/zip/main', | ||
target: '_self', | ||
}) | ||
" | ||
> | ||
Download File | ||
</Button> | ||
</Card> | ||
|
||
<Card class="my-5" title="根据地址下载图片"> | ||
<Button | ||
type="primary" | ||
@click=" | ||
downloadFileFromImageUrl({ | ||
source: | ||
'https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp', | ||
fileName: 'vben-logo.png', | ||
}) | ||
" | ||
> | ||
Download File | ||
</Button> | ||
</Card> | ||
|
||
<Card class="my-5" title="base64流下载"> | ||
<Button | ||
type="primary" | ||
@click=" | ||
downloadFileFromBase64({ | ||
source: imageBase64, | ||
fileName: 'image.png', | ||
}) | ||
" | ||
> | ||
Download Image | ||
</Button> | ||
</Card> | ||
<Card class="my-5" title="文本下载"> | ||
<Button | ||
type="primary" | ||
@click=" | ||
downloadFileFromBlobPart({ | ||
source: 'text content', | ||
fileName: 'test.txt', | ||
}) | ||
" | ||
> | ||
Download TxT | ||
</Button> | ||
</Card> | ||
</Page> | ||
</template> |