-
Notifications
You must be signed in to change notification settings - Fork 468
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
198 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import { ref } from 'vue' | ||
import { createEventHook } from '@vueuse/core' | ||
|
||
const useDownload = () => { | ||
const process = ref(0) | ||
const isDownloading = ref(false) | ||
|
||
const { on: onLoaded, trigger } = createEventHook() | ||
|
||
const getFileExtension = (url: string) => { | ||
const pathname = new URL(url).pathname | ||
const lastDotIndex = pathname.lastIndexOf('.') | ||
if (lastDotIndex === -1) { | ||
return '' | ||
} | ||
return pathname.slice(lastDotIndex + 1) | ||
} | ||
|
||
const getFileName = (url: string) => { | ||
const pathname = new URL(url).pathname | ||
const lastDotIndex = pathname.lastIndexOf('/') | ||
if (lastDotIndex === -1) { | ||
return '未知文件' | ||
} | ||
return pathname.slice(lastDotIndex + 1) | ||
} | ||
|
||
const downloadFile = (url: string, filename?: string, extension?: string) => { | ||
isDownloading.value = true | ||
const xhr = new XMLHttpRequest() | ||
xhr.open('GET', url, true) | ||
xhr.responseType = 'blob' | ||
xhr.onprogress = (event) => { | ||
if (event.lengthComputable) { | ||
const percentComplete = Math.floor((event.loaded / event.total) * 100) | ||
process.value = percentComplete | ||
} | ||
} | ||
xhr.onload = () => { | ||
if (xhr.status === 200) { | ||
const blob = new Blob([xhr.response], { type: 'application/octet-stream' }) | ||
const a = document.createElement('a') | ||
a.href = URL.createObjectURL(blob) | ||
const urlExtension = getFileExtension(url) | ||
const ext = extension || urlExtension | ||
if (filename) { | ||
a.download = `${filename}${ext ? '.' : ''}${ext}` | ||
} else { | ||
const urlFileNmae = getFileName(url) | ||
a.download = urlFileNmae | ||
} | ||
a.click() | ||
// 调用 URL.revokeObjectURL() 方法来释放该内存 | ||
URL.revokeObjectURL(a.href) | ||
trigger('success') | ||
} else { | ||
trigger('fail') | ||
} | ||
// 清空进度 | ||
process.value = 0 | ||
isDownloading.value = false | ||
} | ||
xhr.send() | ||
} | ||
|
||
return { | ||
onLoaded, | ||
downloadFile, | ||
process, | ||
isDownloading, | ||
} | ||
} | ||
|
||
export default useDownload |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import { reactive, watch } from 'vue' | ||
import { defineStore } from 'pinia' | ||
import useDownload from '@/hooks/useDownload' | ||
|
||
type DownloadObjType = { | ||
url: string | ||
isDownloading: boolean | ||
process: number | undefined | ||
} | ||
|
||
// 定义一个下载队列的 store | ||
export const useDownloadQuenuStore = defineStore('downloadQuenu', () => { | ||
// 最多可同时执行下载的任务数量 | ||
const maxDownloadCount = 1 | ||
// 下载队列 | ||
const quenu = reactive<string[]>([]) | ||
// 下载对象 | ||
const downloadObjMap = reactive<Map<string, DownloadObjType>>(new Map()) | ||
|
||
// 添加到下载队列 | ||
const addQuenuAction = (url: string) => { | ||
quenu.push(url) | ||
} | ||
|
||
// 从下载队列中移除 | ||
const removeQuenuAction = (url: string) => { | ||
const index = quenu.indexOf(url) | ||
if (index > -1) { | ||
quenu.splice(index, 1) | ||
} | ||
} | ||
|
||
// 出队列 | ||
const dequeue = () => { | ||
if (!quenu.length || downloadObjMap.size >= maxDownloadCount) { | ||
return | ||
} | ||
const url = quenu.shift() | ||
if (url) { | ||
downloadAction(url) | ||
} | ||
} | ||
|
||
// 下载 | ||
const downloadAction = (url: string) => { | ||
const { downloadFile, isDownloading, process, onLoaded } = useDownload() | ||
const stopWatcher = watch(process, () => { | ||
// 更新下载进度 | ||
downloadObjMap.set(url, { url, isDownloading: isDownloading.value, process: process.value }) | ||
}) | ||
onLoaded(() => { | ||
stopWatcher() // 清除watcher | ||
downloadObjMap.delete(url) // 下载完成后 删除下载对象 | ||
dequeue() | ||
}) | ||
if (url) { | ||
downloadFile(url) | ||
} | ||
} | ||
|
||
const download = (url: string) => { | ||
addQuenuAction(url) | ||
dequeue() | ||
} | ||
|
||
// 取消下载 | ||
const cancelDownload = (url: string) => { | ||
if (quenu.includes(url)) { | ||
removeQuenuAction(url) | ||
} | ||
} | ||
|
||
return { | ||
quenu, | ||
addQuenuAction, | ||
removeQuenuAction, | ||
dequeue, | ||
downloadObjMap, | ||
download, | ||
cancelDownload, | ||
} | ||
}) | ||
|
||
export default useDownloadQuenuStore |