新增工作区下载列表
This commit is contained in:
284
utils/downloadManager.js
Normal file
284
utils/downloadManager.js
Normal file
@@ -0,0 +1,284 @@
|
||||
/**
|
||||
* 下载管理工具
|
||||
* 文件保存到公共下载目录,用户可通过文件管理器访问
|
||||
*/
|
||||
|
||||
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())}`
|
||||
}
|
||||
Reference in New Issue
Block a user