diff --git a/docs/.vitepress/items/util.ts b/docs/.vitepress/items/util.ts index 8af61b8..7dd393c 100644 --- a/docs/.vitepress/items/util.ts +++ b/docs/.vitepress/items/util.ts @@ -1,6 +1,7 @@ export const utilItems = [ { text: 'mitt', link: '/util/mitt' }, { text: 'copyText', link: '/util/copy-text' }, + { text: 'download', link: '/util/download' }, { text: 'storage', link: '/util/storage' }, { text: 'classes', link: '/util/classes' }, { text: 'createNamespaceFn', link: '/util/create-namespace-fn' }, diff --git a/docs/util/download.md b/docs/util/download.md new file mode 100644 index 0000000..03e94d8 --- /dev/null +++ b/docs/util/download.md @@ -0,0 +1,20 @@ +# download + +Trigger browser download, supporting downloading via file `url`, `Blob`, `File`. + +### Usage + +```ts +import { download } from 'rattail' + +download('/hello.txt', 'hello.txt') +download(new Blob(['hello']), 'hello.txt') +download(new File(['helle'], 'hello.txt', { type: 'text/plain' }), 'hello.txt') +``` + +### Arguments + +| Arg | Type | Defaults | +| ---------- | ------------------------ | -------- | +| `value` | `string \| Blob \| File` | | +| `filename` | `string` | | diff --git a/docs/zh/util/download.md b/docs/zh/util/download.md new file mode 100644 index 0000000..347f6b8 --- /dev/null +++ b/docs/zh/util/download.md @@ -0,0 +1,20 @@ +# download + +触发浏览器下载,支持通过 `文件地址`、`Blob`、`File`。 + +### 使用 + +```ts +import { download } from 'rattail' + +download('/hello.txt', 'hello.txt') +download(new Blob(['hello']), 'hello.txt') +download(new File(['helle'], 'hello.txt', { type: 'text/plain' }), 'hello.txt') +``` + +### 参数 + +| 参数 | 类型 | 默认值 | +| ---------- | ------------------------ | ------ | +| `value` | `string \| Blob \| File` | | +| `filename` | `string` | | diff --git a/src/util/download.ts b/src/util/download.ts new file mode 100644 index 0000000..ddb87b2 --- /dev/null +++ b/src/util/download.ts @@ -0,0 +1,13 @@ +import { isString } from '../general' + +export function download(val: string | Blob | File, filename: string = 'file') { + const a = document.createElement('a') + a.style.display = 'none' + a.href = isString(val) ? val : URL.createObjectURL(val) + a.download = filename + + document.body.appendChild(a) + a.click() + URL.revokeObjectURL(a.href) + document.body.removeChild(a) +} diff --git a/src/util/index.ts b/src/util/index.ts index 5ce2814..aad6a56 100644 --- a/src/util/index.ts +++ b/src/util/index.ts @@ -16,3 +16,4 @@ export * from './raf' export * from './requestAnimationFrame' export * from './storage' export * from './tryParseJSON' +export * from './download' diff --git a/tests/util.spec.ts b/tests/util.spec.ts index bdeff63..35066dc 100644 --- a/tests/util.spec.ts +++ b/tests/util.spec.ts @@ -18,6 +18,7 @@ import { tryParseJSON, prettyJSONObject, copyText, + download, } from '../src' it('requestAnimationFrame', () => { @@ -263,3 +264,26 @@ describe('copyText', () => { execCommandMock.mockRestore() }) }) + +it('download', () => { + let href = '' + + Reflect.defineProperty(HTMLAnchorElement.prototype, 'href', { + set(v) { + href = v + }, + get() { + return href + }, + }) + + URL.createObjectURL = vi.fn(() => 'mock') + URL.revokeObjectURL = vi.fn() + download(new Blob(['hello']), 'test.txt') + expect(href).toBe('mock') + expect(URL.createObjectURL).toHaveBeenCalled() + expect(URL.revokeObjectURL).toHaveBeenCalled() + + download('/a.jpg', 'test.txt') + expect(href).toBe('/a.jpg') +})