Files
phone-app------test1-/utils/downloadManager.js
2026-08-03 09:05:21 +08:00

285 lines
6.6 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 下载管理工具
* 文件保存到公共下载目录,用户可通过文件管理器访问
*/
const STORAGE_KEY = 'localDownloads'
const SUB_DIR = 'AppDownloads' // 公共下载目录下的子文件夹
/**
* 获取公共下载目录路径
* @returns {Promise<string>} 下载目录绝对路径
*/
function getPublicDownloadDir() {
return new Promise((resolve, reject) => {
// #ifdef APP-PLUS
plus.io.requestFileSystem(
plus.io.PUBLIC_DOWNLOADS,
(fs) => {
// PUBLIC_DOWNLOADS 对应 /storage/emulated/0/Download/
fs.root.getDirectory(
SUB_DIR,
{ create: true },
(dirEntry) => {
resolve(dirEntry.fullPath)
},
(err) => {
console.error('getDirectory failed:', err)
reject(err)
}
)
},
(err) => {
console.error('requestFileSystem failed:', err)
reject(err)
}
)
// #endif
// #ifndef APP-PLUS
reject(new Error('仅APP端支持'))
// #endif
})
}
/**
* 从完整路径中提取可读的显示路径
* 如 /storage/emulated/0/Download/AppDownloads/xxx.pdf → 下载/AppDownloads/xxx.pdf
*/
function getDisplayPath(fullPath) {
const idx = fullPath.indexOf('/Download/')
if (idx !== -1) {
return '下载/' + fullPath.slice(idx + 10)
}
return fullPath
}
/**
* 获取所有下载记录
* @returns {Array} 下载记录列表
*/
export function getDownloadList() {
try {
return uni.getStorageSync(STORAGE_KEY) || []
} catch (e) {
return []
}
}
/**
* 保存下载记录(复制到公共下载目录)
* @param {string} tempFilePath 临时文件路径uni.downloadFile 返回的)
* @param {string} fileName 文件名
* @param {number} fileSize 文件大小(字节,可选)
*/
export function saveDownload(tempFilePath, fileName, fileSize) {
return new Promise((resolve, reject) => {
// #ifdef APP-PLUS
getPublicDownloadDir().then((dirPath) => {
const destPathForUrl = plus.io.convertLocalFileSystemURL(dirPath + '/' + fileName)
plus.io.resolveLocalFileSystemURL(
tempFilePath,
(fileEntry) => {
plus.io.resolveLocalFileSystemURL(
dirPath,
(dirEntry) => {
fileEntry.copyTo(
dirEntry,
fileName,
() => {
const record = {
id: Date.now().toString(),
name: fileName,
path: destPathForUrl,
displayPath: getDisplayPath(destPathForUrl),
size: fileSize || 0,
time: Date.now()
}
const list = getDownloadList()
const exists = list.some(item => item.name === fileName)
if (exists) {
const idx = list.findIndex(item => item.name === fileName)
if (idx !== -1) list.splice(idx, 1)
}
list.unshift(record)
try {
uni.setStorageSync(STORAGE_KEY, list)
} catch (e) {
list.pop()
uni.setStorageSync(STORAGE_KEY, list)
}
resolve(record)
},
(err) => {
console.error('copyTo failed:', err)
reject(err)
}
)
},
reject
)
},
reject
)
}).catch(reject)
// #endif
// #ifndef APP-PLUS
// 非 APP 端降级使用 uni.saveFile
uni.saveFile({
tempFilePath: tempFilePath,
success: (saveRes) => {
const record = {
id: Date.now().toString(),
name: fileName,
path: saveRes.savedFilePath,
size: fileSize || 0,
time: Date.now()
}
const list = getDownloadList()
list.unshift(record)
uni.setStorageSync(STORAGE_KEY, list)
resolve(record)
},
fail: reject
})
// #endif
})
}
/**
* 删除单条下载记录及其文件
* @param {string} id 记录 ID
*/
export function deleteDownload(id) {
return new Promise((resolve, reject) => {
const list = getDownloadList()
const record = list.find(item => item.id === id)
if (!record) {
reject(new Error('记录不存在'))
return
}
// #ifdef APP-PLUS
// 删除公共目录下的文件
plus.io.resolveLocalFileSystemURL(
record.path,
(fileEntry) => {
fileEntry.remove(
() => {
const newList = list.filter(item => item.id !== id)
uni.setStorageSync(STORAGE_KEY, newList)
resolve()
},
() => {
// 文件不存在也清除记录
const newList = list.filter(item => item.id !== id)
uni.setStorageSync(STORAGE_KEY, newList)
resolve()
}
)
},
() => {
// 无法解析路径,直接清除记录
const newList = list.filter(item => item.id !== id)
uni.setStorageSync(STORAGE_KEY, newList)
resolve()
}
)
// #endif
// #ifndef APP-PLUS
uni.removeSavedFile({
filePath: record.path,
success: () => {
const newList = list.filter(item => item.id !== id)
uni.setStorageSync(STORAGE_KEY, newList)
resolve()
},
fail: () => {
const newList = list.filter(item => item.id !== id)
uni.setStorageSync(STORAGE_KEY, newList)
resolve()
}
})
// #endif
})
}
/**
* 清空所有下载记录和文件
*/
export function clearAllDownloads() {
return new Promise((resolve) => {
const list = getDownloadList()
if (list.length === 0) {
resolve()
return
}
// #ifdef APP-PLUS
let count = 0
const done = () => {
count++
if (count >= list.length) {
uni.setStorageSync(STORAGE_KEY, [])
resolve()
}
}
list.forEach(record => {
plus.io.resolveLocalFileSystemURL(
record.path,
(fileEntry) => {
fileEntry.remove(done, done)
},
done
)
})
setTimeout(() => {
uni.setStorageSync(STORAGE_KEY, [])
resolve()
}, 5000)
// #endif
// #ifndef APP-PLUS
let count = 0
list.forEach(record => {
uni.removeSavedFile({
filePath: record.path,
success: () => { count++; if (count >= list.length) { uni.setStorageSync(STORAGE_KEY, []); resolve() } },
fail: () => { count++; if (count >= list.length) { uni.setStorageSync(STORAGE_KEY, []); resolve() } }
})
})
if (list.length === 0) resolve()
setTimeout(() => { uni.setStorageSync(STORAGE_KEY, []); resolve() }, 3000)
// #endif
})
}
/**
* 格式化文件大小为可读字符串
* @param {number} bytes 字节数
* @returns {string}
*/
export function formatFileSize(bytes) {
if (!bytes || bytes <= 0) return '未知大小'
const units = ['B', 'KB', 'MB', 'GB']
let i = 0
let size = bytes
while (size >= 1024 && i < units.length - 1) {
size /= 1024
i++
}
return size.toFixed(i === 0 ? 0 : 1) + ' ' + units[i]
}
/**
* 格式化时间戳为可读日期
* @param {number} timestamp
* @returns {string}
*/
export function formatTime(timestamp) {
const d = new Date(timestamp)
const pad = n => String(n).padStart(2, '0')
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
}