diff --git a/app/(tabs)/owner.tsx b/app/(tabs)/owner.tsx
index 5ab4693..1c6ec85 100644
--- a/app/(tabs)/owner.tsx
+++ b/app/(tabs)/owner.tsx
@@ -71,7 +71,7 @@ export default function OwnerPage() {
{/* 资源数据 */}
- } style={{ flex: 1 }} isFormatBytes={true} />
+ } style={{ flex: 1 }} isFormatBytes={true} />
} style={{ flex: 1 }} />
{/* 数据统计 */}
diff --git a/components/file-upload/backgroundUploader.ts b/components/file-upload/backgroundUploader.ts
index 274d9d8..4582dca 100644
--- a/components/file-upload/backgroundUploader.ts
+++ b/components/file-upload/backgroundUploader.ts
@@ -4,6 +4,7 @@ import * as FileSystem from 'expo-file-system';
import * as ImageManipulator from 'expo-image-manipulator';
import * as MediaLibrary from 'expo-media-library';
import * as TaskManager from 'expo-task-manager';
+import pLimit from 'p-limit';
import { Alert } from 'react-native';
import { transformData } from '../utils/objectFlat';
@@ -14,6 +15,30 @@ type ExtendedAsset = MediaLibrary.Asset & {
const BACKGROUND_UPLOAD_TASK = 'background-upload-task';
// 设置最大并发数
const CONCURRENCY_LIMIT = 10; // 同时最多上传10个文件
+// 在 CONCURRENCY_LIMIT 定义后添加
+const limit = pLimit(CONCURRENCY_LIMIT);
+
+// 获取文件扩展名
+const getFileExtension = (filename: string) => {
+ return filename.split('.').pop()?.toLowerCase() || '';
+};
+
+// 获取 MIME 类型
+const getMimeType = (filename: string, isVideo: boolean) => {
+ if (!isVideo) return 'image/jpeg';
+
+ const ext = getFileExtension(filename);
+ switch (ext) {
+ case 'mov':
+ return 'video/quicktime';
+ case 'mp4':
+ return 'video/mp4';
+ case 'm4v':
+ return 'video/x-m4v';
+ default:
+ return 'video/mp4'; // 默认值
+ }
+};
// 将 HEIC 图片转化
const convertHeicToJpeg = async (uri: string): Promise => {
@@ -274,42 +299,67 @@ const processMediaUpload = async (asset: ExtendedAsset) => {
const uploadOriginalFile = async () => {
try {
let fileToUpload: File;
- let mimeType = isVideo ? 'video/mp4' : 'image/jpeg';
+ const isVideo = asset.mediaType === 'video';
+ const mimeType = getMimeType(asset.filename || '', isVideo);
+
+ // 生成文件名,保留原始扩展名
let filename = asset.filename ||
- `${isVideo ? 'video' : 'image'}_${Date.now()}_original.${isVideo ? 'mp4' : 'jpg'}`;
+ `${isVideo ? 'video' : 'image'}_${Date.now()}_original.${isVideo ? (getFileExtension(asset.filename || 'mp4') || 'mp4') : 'jpg'}`;
// 处理 HEIC 格式
if (filename.toLowerCase().endsWith('.heic') || filename.toLowerCase().endsWith('.heif')) {
- try {
- fileToUpload = await convertHeicToJpeg(asset.uri);
- filename = filename.replace(/\.(heic|heif)$/i, '.jpg');
- } catch (conversionError) {
- console.error('HEIC conversion failed, trying fallback:', conversionError);
- // 如果转换失败,尝试使用 MediaLibrary 获取文件
- try {
- const assetInfo = await MediaLibrary.getAssetInfoAsync(asset.id);
- if (assetInfo.localUri) {
- const response = await fetch(assetInfo.localUri);
- const blob = await response.blob();
- fileToUpload = new File([blob], filename, { type: 'image/jpeg' });
- } else {
- throw new Error('No local URI available for the asset');
- }
- } catch (fallbackError) {
- console.error('Fallback method also failed:', fallbackError);
- // 如果所有方法都失败,尝试直接使用原始 URI
- const response = await fetch(asset.uri);
- const blob = await response.blob();
- fileToUpload = new File([blob], filename, { type: 'image/jpeg' });
- }
- }
+ fileToUpload = await convertHeicToJpeg(asset.uri);
+ filename = filename.replace(/\.(heic|heif)$/i, '.jpg');
} else {
- // 普通图片处理
- const response = await fetch(asset.uri);
+ // 获取资源信息
+ const assetInfo = await MediaLibrary.getAssetInfoAsync(asset.id, {
+ shouldDownloadFromNetwork: true
+ });
+
+ if (!assetInfo.localUri) {
+ throw new Error('无法获取资源的本地路径');
+ }
+
+ // 获取文件扩展名
+ const fileExtension = getFileExtension(assetInfo.filename || '') ||
+ (isVideo ? 'mp4' : 'jpg');
+
+ // 确保文件名有正确的扩展名
+ if (!filename.toLowerCase().endsWith(`.${fileExtension}`)) {
+ const baseName = filename.split('.')[0];
+ filename = `${baseName}.${fileExtension}`;
+ }
+
+ // 获取文件内容
+ const response = await fetch(assetInfo.localUri);
const blob = await response.blob();
+
+ // 创建文件对象
fileToUpload = new File([blob], filename, { type: mimeType });
+ console.log('文件准备上传:', {
+ name: fileToUpload.name,
+ type: fileToUpload.type,
+ size: fileToUpload.size
+ });
}
- let exifData = asset?.exif ? { ...transformData({ ...asset, exif: { ...asset?.exif, '{MakerApple}': undefined } }) } : {};
+
+ // 准备元数据
+ let exifData = {};
+ if (asset.exif) {
+ try {
+ exifData = transformData({
+ ...asset,
+ exif: {
+ ...asset.exif,
+ '{MakerApple}': undefined
+ }
+ });
+ } catch (exifError) {
+ console.warn('处理 EXIF 数据时出错:', exifError);
+ }
+ }
+
+ // 获取上传 URL
const { upload_url, file_id } = await getUploadUrl(fileToUpload, {
originalUri: asset.uri,
creationTime: asset.creationTime,
@@ -319,26 +369,31 @@ const processMediaUpload = async (asset: ExtendedAsset) => {
GPSVersionID: undefined
});
+ // 上传文件
await uploadFile(fileToUpload, upload_url);
await confirmUpload(file_id);
- return { success: true, file_id };
- } catch (error: unknown) {
- if (error instanceof Error) {
- console.error('Error in uploadOriginalFile:', {
- message: error.message,
- name: error.name,
- stack: error.stack,
- asset: {
- id: asset.id,
- uri: asset.uri,
- filename: asset.filename
- }
- });
- } else {
- console.error('An unknown error occurred:', error);
- }
- throw error;
+ console.log('文件上传成功:', {
+ fileId: file_id,
+ filename: fileToUpload.name,
+ type: fileToUpload.type
+ });
+
+ return {
+ success: true,
+ file_id,
+ filename: fileToUpload.name
+ };
+
+ } catch (error: any) {
+ const errorMessage = error instanceof Error ? error.message : '未知错误';
+ console.error('上传原始文件时出错:', {
+ error: errorMessage,
+ assetId: asset.id,
+ filename: asset.filename,
+ uri: asset.uri
+ });
+ throw new Error(`上传失败: ${errorMessage}`);
}
};
@@ -467,62 +522,51 @@ export const triggerManualUpload = async (startDate: Date, endDate: Date) => {
// 分离图片和视频
const photos = media.filter(item => item.mediaType === 'photo');
const videos = media.filter(item => item.mediaType === 'video');
+ console.log('videos11111111', videos);
- const results = [];
+ const results: any[] = [];
- // 先处理所有图片
- for (const item of photos) {
+ // 处理所有图片(带并发控制)
+ const processPhoto = async (item: any) => {
try {
const result = await processMediaUpload(item);
results.push({
id: item.id,
...result
});
- } catch (error: unknown) {
- const errorMessage = error instanceof Error ? error.message : 'An unknown error occurred';
- console.error('Error processing photo upload:', {
- error: error instanceof Error ? {
- message: error.message,
- name: error.name,
- stack: error.stack
- } : error,
- assetId: item.id
- });
+ } catch (error: any) {
results.push({
id: item.id,
originalSuccess: false,
compressedSuccess: false,
- error: errorMessage
+ error: error.message
});
}
- }
+ };
- // 再处理所有视频
- for (const item of videos) {
+ // 处理所有视频(带并发控制)
+ const processVideo = async (item: any) => {
try {
const result = await processMediaUpload(item);
results.push({
id: item.id,
...result
});
- } catch (error: unknown) {
- const errorMessage = error instanceof Error ? error.message : 'An unknown error occurred';
- console.error('Error processing video upload:', {
- error: error instanceof Error ? {
- message: error.message,
- name: error.name,
- stack: error.stack
- } : error,
- assetId: item.id
- });
+ } catch (error: any) {
results.push({
id: item.id,
originalSuccess: false,
compressedSuccess: false,
- error: errorMessage
+ error: error.message
});
}
- }
+ };
+
+ // 并发处理图片和视频
+ await Promise.all([
+ ...photos.map(photo => limit(() => processPhoto(photo))),
+ ...videos.map(video => limit(() => processVideo(video)))
+ ]);
return results;
} catch (error) {
diff --git a/components/owner/userName.tsx b/components/owner/userName.tsx
index ef3beff..ab64c68 100644
--- a/components/owner/userName.tsx
+++ b/components/owner/userName.tsx
@@ -57,7 +57,7 @@ export default function UserInfo({ userInfo }: { userInfo: UserInfoDetails }) {
{userInfo?.user_info?.avatar_file_url
?
: