84 lines
2.4 KiB
TypeScript
84 lines
2.4 KiB
TypeScript
import { useCallback, useState } from 'react'
|
|
import * as FileSystem from 'expo-file-system'
|
|
import * as MediaLibrary from 'expo-media-library'
|
|
|
|
export type MediaType = 'image' | 'video'
|
|
|
|
export interface DownloadResult {
|
|
success: boolean
|
|
error?: string
|
|
}
|
|
|
|
export const useDownloadMedia = () => {
|
|
const [loading, setLoading] = useState(false)
|
|
const [error, setError] = useState<string | null>(null)
|
|
const [progress, setProgress] = useState(0)
|
|
|
|
const download = useCallback(async (url: string, type: MediaType): Promise<DownloadResult> => {
|
|
setLoading(true)
|
|
setError(null)
|
|
setProgress(0)
|
|
|
|
let fileUri: string | null = null
|
|
|
|
try {
|
|
// 1. 请求媒体库权限
|
|
const { status } = await MediaLibrary.requestPermissionsAsync()
|
|
if (status !== 'granted') {
|
|
setError('Permission denied')
|
|
setLoading(false)
|
|
return { success: false, error: 'Permission denied' }
|
|
}
|
|
|
|
// 2. 生成临时文件名
|
|
const extension = type === 'video' ? 'mp4' : 'jpg'
|
|
const fileName = `download_${Date.now()}.${extension}`
|
|
fileUri = FileSystem.documentDirectory + fileName
|
|
|
|
// 3. 下载文件(带进度)
|
|
const downloadResumable = FileSystem.createDownloadResumable(
|
|
url,
|
|
fileUri,
|
|
{},
|
|
(downloadProgress) => {
|
|
const progressValue = downloadProgress.totalBytesWritten / downloadProgress.totalBytesExpectedToWrite
|
|
setProgress(progressValue)
|
|
}
|
|
)
|
|
|
|
const downloadResult = await downloadResumable.downloadAsync()
|
|
if (!downloadResult?.uri) {
|
|
throw new Error('Download failed')
|
|
}
|
|
|
|
// 4. 保存到相册
|
|
await MediaLibrary.saveToLibraryAsync(downloadResult.uri)
|
|
|
|
// 5. 清理临时文件
|
|
await FileSystem.deleteAsync(fileUri, { idempotent: true })
|
|
|
|
setLoading(false)
|
|
setProgress(0)
|
|
return { success: true }
|
|
} catch (e) {
|
|
const errorMessage = e instanceof Error ? e.message : 'Download failed'
|
|
setError(errorMessage)
|
|
setLoading(false)
|
|
setProgress(0)
|
|
|
|
// 尝试清理临时文件
|
|
if (fileUri) {
|
|
try {
|
|
await FileSystem.deleteAsync(fileUri, { idempotent: true })
|
|
} catch {
|
|
// 忽略清理错误
|
|
}
|
|
}
|
|
|
|
return { success: false, error: errorMessage }
|
|
}
|
|
}, [])
|
|
|
|
return { download, loading, error, progress }
|
|
}
|