expo-popcore-app/hooks/use-download-media.ts

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 }
}