支付宝小程序camera录制视频并上传注意事项

1.默认30秒超时,超时后调用cameraContext.stopRecord时,stopRecord回调不会触发,最高timeout设置60秒

2.录制后的视频在真机看时是.video后缀,实际上传给后端时是类似:0.dc。后端可直接改后缀为mp4格式,因为文件mime类型还是mp4。

3.持久化存储或前端重命名时调用saveFile会报错10024:指定的路径没有写权限,我也不知道为啥,保存图片则没问题。我用copyFile解决。

4.copyFile最大只能复制30M的文件,超过会报错。持久化存储后需要注意删除文件,getFileSystemManager最多只能保存200M,只有主动删除或删除小程序时才会清除持久化的文件

uni代码示例:

bash 复制代码
<camera 
  id="camera"
  output-dimension="720P"
  class="camera-view"
  device-position="back"
  flash="off"
  @error="onCameraError"
></camera>

const startRecording = async () => {
  if (!cameraContext) {
    // #ifdef MP-ALIPAY
    cameraContext = my.createCameraContext("camera");
    // #else
    cameraContext = uni.createCameraContext();
    // #endif
  }
  
  isRecording.value = true;
  recordingTime.value = 0;
  recordingTimeText.value = '00:00';
  // console.log('开始录制视频');
  cameraContext.startRecord({
    timeout: 600, // camera支付宝最多只能拍摄10分钟,微信5分钟,单位秒
    success: () => {
      // console.log('正在录制视频');
      startTimer();
    },
    fail: () => {
      // console.error('Start record fail:', e);
      isRecording.value = false;
    },
    timeoutCallback: () => {
      // console.log('Recording timeout');
      isRecording.value = false;
      uni.showToast({ title: '录制超时', icon: 'none' });
    }
  });
};

const stopRecording = () => {
  if (!cameraContext) return;
  cameraContext.stopRecord({
    // compressed: true,
    success: (res: any) => {
      console.log('录制视频信息:', res)
      isRecording.value = false;
      stopTimer();
      handleRecordedVideo(res.tempVideoPath);
      // handleRecordedVideo(res.tempVideoPath.replace(".video", ".mp4"));

    },
    fail: (e: any) => {
      console.error('Stop record fail', e);
      uni.showToast({ title: e.message || '停止录制失败', icon: 'none' });
    }
  });
};

// 保存临时文件到本地持久化路径
export const saveTempToLocal = (tempFilePath: string, fileType?: string): Promise<string> => {
  console.log('保存临时文件到本地', tempFilePath, fileType);
  return new Promise((resolve, reject) => {
    // 获取原始后缀
    const extIndex = tempFilePath.lastIndexOf('.');
    let ext = tempFilePath.substring(extIndex).toLowerCase();
    
    // 处理支付宝小程序临时文件后缀问题
    // 支付宝拍照返回的临时文件可能是 .image 后缀,需要根据文件类型转换
    const invalidExts = ['.image', '.temp', '.tmp', '.video'];
    if (invalidExts.includes(ext)) {
      // 根据文件类型设置正确的后缀
      if (fileType === 'video') {
        ext = '.mp4';
      } else {
        ext = '.jpg';
      }
    }
    
    const fileName = `upload_${Date.now()}_${Math.random().toString(36).slice(2)}${ext}`;
    const newPath = `${my.env.USER_DATA_PATH}/upload_${Date.now()}.mp4`;
    
    //#ifdef MP-ALIPAY
    // 支付宝小程序使用 my.getFileSystemManager
    const fs = my.getFileSystemManager();
    const targetPath = `${my.env.USER_DATA_PATH}/${fileName}`;
    if (fileType === 'video') {
      // 由于视频调用saveFile会报错10024:指定的路径没有写权限,所以这里使用copyFile保存
      fs.copyFile({
        srcPath: tempFilePath,
        destPath: newPath,
        success: () => {
          console.log('视频复制后路径', newPath);
          resolve(newPath);
        },
        fail: (err: any) => {
          if (err.error == 10028) {
          // IOS超过30M无法复制
            uni.showToast({ title: '视频文件过大,无法保存', icon: 'none' });
          } else {
            uni.showToast({ title: err.message || '处理视频文件失败', icon: 'none' });
          }
          console.error('复制视频文件失败:', err);
          reject(err);
        }
      });
    } else {
      fs.saveFile({
        tempFilePath: tempFilePath,
        filePath: targetPath,
        success: (res: any) => {
          console.log('文件保存成功', res.savedFilePath, tempFilePath, targetPath);
          resolve(res.savedFilePath);
        },
        fail: (err: any) => {
          console.error('文件保存失败', err, tempFilePath, targetPath);
          uni.showToast({ title: err.message || '文件保存失败', icon: 'none' });
          reject(err);
        }
      });
    }
    
    //#endif
    
    //#ifdef MP-WEIXIN
    // 微信小程序使用 uni.saveFile
    uni.saveFile({
      tempFilePath: tempFilePath,
      success: (res) => {
        console.log('文件保存成功', res.savedFilePath);
        resolve(res.savedFilePath);
      },
      fail: (err) => {
        console.error('文件保存失败', err);
        reject(err);
      }
    });
    //#endif
    
    //#ifndef MP-ALIPAY
    //#ifndef MP-WEIXIN
    // 其他平台先直接返回临时路径
    console.warn('当前平台不支持临时文件持久化,直接使用临时路径');
    resolve(tempFilePath);
    //#endif
    //#endif
  });
}

// 删除本地持久化文件
export const deleteLocalFile = (filePath: string): Promise<void> => {
  return new Promise((resolve, reject) => {
    try {
      //#ifdef MP-ALIPAY
      const fs = my.getFileSystemManager();
      fs.unlink({
        filePath: filePath,
        success: () => {
          console.log('文件删除成功', filePath);
          resolve();
        },
        fail: (err: any) => {
          console.error('文件删除失败', err);
          reject(err);
        }
      });
      //#endif
      
      //#ifdef MP-WEIXIN
      uni.removeSavedFile({
        filePath: filePath,
        success: () => {
          console.log('文件删除成功', filePath);
          resolve();
        },
        fail: (err) => {
          console.error('文件删除失败', err);
          reject(err);
        }
      });
      //#endif
      
      //#ifndef MP-ALIPAY
      //#ifndef MP-WEIXIN
      // 其他平台不做删除操作
      resolve();
      //#endif
      //#endif
    } catch (error) {
      console.error('删除本地文件失败', error);
      reject(error);
    }
  });
}
相关推荐
肥or胖2 小时前
Qt中OpenGL快速入门
qt·音视频·opengl
byte轻骑兵2 小时前
【AVRCP】规范精讲[20]: 播放器设置全打通,让车载与手机的播放控制完全同步
智能手机·音视频·avrcp·音视频控制·车机蓝牙
2601_957884842 小时前
AI赋能的内容工程学:短视频矩阵系统的多模态内容生成与量产边界
人工智能·矩阵·音视频
小橙讲编程3 小时前
MoneyPrinterTurbo 深度解析与部署实战:AI 一键短视频生成,从源码到上线全攻略
人工智能·音视频
leduo668899o3 小时前
知识付费系统深度测评:7款平台,内容加密+视频水印功能实测对比
大数据·网络·音视频
美狐美颜SDK开放平台4 小时前
低延迟+高清美颜:直播APP开发中的音视频与美颜SDK优化方案
人工智能·音视频·美颜sdk·直播美颜sdk·第三方美颜sdk·短视频美颜sdk
我是伪码农5 小时前
小程序125-150
小程序
帅次5 小时前
讯飞与腾讯云:Android 实时语音识别服务对比选择
android·ios·微信小程序·小程序·android studio·android runtime