import * as MediaLibrary from 'expo-media-library'; import React, { useState } from 'react'; import { ActivityIndicator, Alert, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; interface MediaStats { total: number; photos: number; videos: number; audios: number; others: number; byMonth: Record; } type TimeRange = 'day' | 'week' | 'month' | 'all'; const MediaStatsScreen = () => { const [isLoading, setIsLoading] = useState(false); const [stats, setStats] = useState(null); const [timeRange, setTimeRange] = useState('week'); // 默认显示一周 const getDateRange = (range: TimeRange) => { const now = new Date(); const start = new Date(now); switch (range) { case 'day': start.setDate(now.getDate() - 1); break; case 'week': start.setDate(now.getDate() - 7); break; case 'month': start.setMonth(now.getMonth() - 1); break; case 'all': default: return null; // 返回 null 表示不限制时间范围 } return { start, end: now }; }; const getMediaStatistics = async () => { setIsLoading(true); try { // 1. 请求媒体库权限 const { status } = await MediaLibrary.requestPermissionsAsync(); if (status !== 'granted') { Alert.alert('权限被拒绝', '需要访问媒体库权限来获取统计信息'); return; } // 2. 设置时间范围 const dateRange = getDateRange(timeRange); const createdAfter = dateRange ? Math.floor(dateRange.start.getTime() / 1000) : 0; const endTime = dateRange?.end ? Math.floor(dateRange.end.getTime() / 1000) : undefined; // 3. 分页获取媒体资源,每次10条 let hasNextPage = true; let after = undefined; let allAssets: MediaLibrary.Asset[] = []; const pageSize = 10; // 每次获取10条 while (hasNextPage) { const media = await MediaLibrary.getAssetsAsync({ first: pageSize, after, mediaType: ['photo', 'video', 'audio', 'unknown'], sortBy: 'creationTime', // 按创建时间降序,最新的在前面 createdAfter: Date.now() - 24 * 30 * 12 * 60 * 60 * 1000, // 时间戳(毫秒) createdBefore: Date.now(), // 时间戳(毫秒) }); // 如果没有数据,直接退出 if (media.assets.length === 0) { break; } // 检查每条记录是否在时间范围内 for (const asset of media.assets) { const assetTime = asset.creationTime ? new Date(asset.creationTime).getTime() / 1000 : 0; // 如果设置了结束时间,并且当前记录的时间早于开始时间,则停止 if (endTime && assetTime > endTime) { continue; // 跳过这条记录 } // 如果设置了开始时间,并且当前记录的时间早于开始时间,则停止 if (createdAfter && assetTime < createdAfter) { hasNextPage = false; break; } allAssets.push(asset); } // 更新游标和是否还有下一页 hasNextPage = media.hasNextPage && media.assets.length === pageSize; after = media.endCursor; // 如果没有更多数据或者已经获取了足够的数据 if (!hasNextPage || allAssets.length >= 1000) { break; } } console.log(`总共获取到 ${allAssets.length} 个媒体文件`); // 4. 统计不同类型媒体的数量 const stats: MediaStats = { total: allAssets.length, photos: 0, videos: 0, audios: 0, others: 0, byMonth: {}, }; allAssets.forEach(asset => { // 统计类型 if (asset.mediaType === 'photo') stats.photos++; else if (asset.mediaType === 'video') stats.videos++; else if (asset.mediaType === 'audio') stats.audios++; else stats.others++; // 按月份统计 if (asset.creationTime) { const date = new Date(asset.creationTime); const monthKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`; stats.byMonth[monthKey] = (stats.byMonth[monthKey] || 0) + 1; } }); setStats(stats); } catch (error) { console.error('获取媒体库统计信息失败:', error); Alert.alert('错误', '获取媒体库统计信息失败'); } finally { setIsLoading(false); } }; // 时间范围选择器 const TimeRangeSelector = () => ( {(['day', 'week', 'month', 'all'] as TimeRange[]).map((range) => ( { setTimeRange(range); }} disabled={isLoading} > {{ day: '今天', week: '本周', month: '本月', all: '全部' }[range]} ))} ); return ( 媒体库统计 {isLoading ? ( ) : ( 获取媒体库统计 )} {stats && ( 按月统计 {Object.entries(stats.byMonth) .sort(([a], [b]) => b.localeCompare(a)) .map(([month, count]) => ( {month} {count} 个文件 ))} )} ); }; const StatItem = ({ label, value }: { label: string; value: string }) => ( {value} {label} ); const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#fff', padding: 16, }, header: { marginBottom: 16, alignItems: 'center', }, title: { fontSize: 20, fontWeight: 'bold', color: '#333', }, timeRangeContainer: { flexDirection: 'row', justifyContent: 'space-between', marginBottom: 16, paddingHorizontal: 8, }, timeRangeButton: { paddingVertical: 8, paddingHorizontal: 16, borderRadius: 16, backgroundColor: '#f0f0f0', }, timeRangeButtonActive: { backgroundColor: '#007AFF', }, timeRangeButtonText: { color: '#666', fontSize: 14, }, timeRangeButtonTextActive: { color: '#fff', fontWeight: '600', }, button: { backgroundColor: '#007AFF', paddingVertical: 12, borderRadius: 8, alignItems: 'center', marginBottom: 20, }, buttonDisabled: { opacity: 0.6, }, buttonText: { color: '#fff', fontSize: 16, fontWeight: '600', }, statsContainer: { backgroundColor: '#f8f8f8', borderRadius: 12, padding: 16, }, statsRow: { flexDirection: 'row', justifyContent: 'space-between', marginBottom: 16, }, statItem: { alignItems: 'center', flex: 1, }, statValue: { fontSize: 20, fontWeight: 'bold', color: '#007AFF', marginBottom: 4, }, statLabel: { fontSize: 12, color: '#666', }, monthlyContainer: { marginTop: 16, }, sectionTitle: { fontSize: 16, fontWeight: '600', color: '#333', marginBottom: 12, }, monthlyItem: { flexDirection: 'row', justifyContent: 'space-between', paddingVertical: 10, borderBottomWidth: 1, borderBottomColor: '#eee', }, monthText: { fontSize: 14, color: '#333', }, countText: { fontSize: 14, color: '#666', }, }); export default MediaStatsScreen;