uniapp APP端 ios保存图片报错办理方案

打印 上一主题 下一主题

主题 786|帖子 786|积分 2358

1、报错信息

  1. {
  2.     "errMsg": "saveImageToPhotosAlbum:fail [Gallery:3302]未能完成操作。(PHPhotosErrorDomain错误3302。),",
  3.     "errCode": -100,
  4.     "code": -100
  5. }
复制代码
2、分析

在安卓端测试uni.saveImageToPhotosAlbum保存图片正常,在ios端报错,缘故起因大概是:因为后端对于预览图片的接口直接是通过传入文件id,然后后端查询到路径,然后回写图片数据,没有利用nginx,导致图片的url没有后缀,uniapp通过uni.downloadFile下载到暂时路径没有后缀名,安卓正常,ios报错。
3、办理方案

办理方案大抵分为两步:
(1)、通过downloadFile的暂时路径然后再进行一次另存,增加后缀名
(2)、然后调用saveImageToPhotosAlbum进行保存
关键方法如下:
  1.     /**
  2.      * 重命名文件,加上指定后缀
  3.      * @param {string} oldPath - 原文件路径
  4.      * @param {string} fileSuffix - 文件后缀,例如 '.jpg'
  5.      * @returns {Promise<string>} - 返回重命名后的文件路径字符串
  6.      */
  7.     renameImage(oldPath, fileSuffix) {
  8.       return new Promise((resolve, reject) => {
  9.         plus.io.resolveLocalFileSystemURL(
  10.           oldPath,
  11.           (entry) => {
  12.             entry.getParent(
  13.               (parentEntry) => {
  14.                 const newName = entry.name + fileSuffix;
  15.                 entry.moveTo(
  16.                   parentEntry,
  17.                   newName,
  18.                   (newFileEntry) => {
  19.                     resolve(newFileEntry.fullPath);
  20.                   },
  21.                   (err) => {
  22.                     console.error("文件移动失败", err);
  23.                     reject(`文件移动失败: ${err.message}`);
  24.                   }
  25.                 );
  26.               },
  27.               (err) => {
  28.                 console.error("获取父目录失败", err);
  29.                 reject(`获取父目录失败: ${err.message}`);
  30.               }
  31.             );
  32.           },
  33.           (err) => {
  34.             console.error("解析文件路径失败", err);
  35.             reject(`解析文件路径失败: ${err.message}`);
  36.           }
  37.         );
  38.       });
  39.     },
复制代码
  1.     // 异步保存图片方法
  2.     async saveImage() {
  3.       try {
  4.         // 直接下载图片
  5.         const downloadResult = await new Promise((resolve, reject) => {
  6.           uni.downloadFile({
  7.             url: this.imageUrl,
  8.             success: resolve,
  9.             fail: reject,
  10.           });
  11.         });
  12.         if (downloadResult.statusCode === 200) {
  13.           // 下载成功后保存图片到相册
  14.           const newPath = await this.renameImage(
  15.             downloadResult.tempFilePath,
  16.             this.fileSuffix
  17.           );
  18.           await new Promise((resolve, reject) => {
  19.             uni.saveImageToPhotosAlbum({
  20.               filePath: newPath,
  21.               success: resolve,
  22.               fail: reject,
  23.             });
  24.           });
  25.           uni.showToast({
  26.             title: "保存成功",
  27.             icon: "success",
  28.           });
  29.           this.closeOptionsPopup();
  30.         } else {
  31.           uni.showToast({
  32.             title: "保存失败",
  33.             icon: "none",
  34.           });
  35.           throw new Error("下载失败");
  36.         }
  37.       } catch (err) {
  38.         console.error("操作失败", err);
  39.         uni.showToast({
  40.           title: "操作失败",
  41.           icon: "none",
  42.         });
  43.       }
  44.     },
复制代码
4、整体页面代码

  1. <template>  <view class="notify">    <uni-card      v-for="(item, index) in notifyList"      :key="index"      :title="        findDataInfo(          dictData.notification_categories,          item.attachTag,          'value',          'text'        )      "    >      <text class="uni-body">        {{ item.attachName }}      </text>      <!-- <uni-list>        <uni-list-item title="查看附件" showArrow></uni-list-item>      </uni-list> -->      <view slot="actions" class="card-actions no-border">        <view          class="card-actions-item"          @click="viewAttachments(item.attachId, item.attachExt)"        >          <uni-icons type="bars" size="18" color="#999"></uni-icons>          <text class="card-actions-item-text">查看附件</text>        </view>      </view>    </uni-card>    <!-- 图片预览的弹出框 -->    <uni-popup ref="popup" type="center" :is-mask-click="false">      <view class="popup-container">        <view class="image-wrapper">          <image            :src="imageUrl"            mode="aspectFit"            class="preview-image"            @longpress="openOptionsPopup"          ></image>        </view>        <uni-icons          type="closeempty"          size="25"          class="close-button"          @click="closeImagePopup"        ></uni-icons>      </view>    </uni-popup>    <!-- 保存图片选项的弹出框 -->    <uni-popup ref="optionsPopup" type="bottom" :is-mask-click="true">      <view class="options-container">        <view class="option-item" @click="saveImage">保存图片</view>        <!-- <view class="separator"></view> -->        <view class="gap"></view>        <view class="cancel-item" @click="closeOptionsPopup">取消</view>      </view>    </uni-popup>  </view></template><script>import { queryNotifyList } from "@/api/work/notify";import { getDictsByCodeValue } from "@/api/index";import { toast } from "@/utils/common";import config from "@/config";export default {  components: {},  data() {    return {      //字典值      dicts: [        {          codeName: "notification_categories",          codeId: "NotificationCategories",        },      ],      dictMap: new Map(),      //字典数据      dictData: {},      notifyList: [],      imageUrl: "",      fileSuffix: "",    };  },  computed: {},  methods: {    //查询关照列表    getNotifyList() {      const param = {        pageNumber: 1,        pageSize: 10000,        params: {          attachType: "01",        },      };      queryNotifyList(param).then((res) => {        this.notifyList = res.rows;      });    },    //查看附件    viewAttachments(attachId, attachExt) {      if (!attachId) {        toast("暂无附件");        return;      }      const baseUrl = config.baseUrl;      if (attachExt === ".pdf") {        this.$tab.navigateTo(          "/pages/common/pdfview/index?url=" +            baseUrl +            "/rest/platform/attachment/showPdf?picId=" +            attachId        );      } else {        this.imageUrl =          baseUrl + "/rest/platform/attachment/showPic?picId=" + attachId;        this.fileSuffix = attachExt;        this.openImagePopup();      }    },    // 异步保存图片方法
  2.     async saveImage() {
  3.       try {
  4.         // 直接下载图片
  5.         const downloadResult = await new Promise((resolve, reject) => {
  6.           uni.downloadFile({
  7.             url: this.imageUrl,
  8.             success: resolve,
  9.             fail: reject,
  10.           });
  11.         });
  12.         if (downloadResult.statusCode === 200) {
  13.           // 下载成功后保存图片到相册
  14.           const newPath = await this.renameImage(
  15.             downloadResult.tempFilePath,
  16.             this.fileSuffix
  17.           );
  18.           await new Promise((resolve, reject) => {
  19.             uni.saveImageToPhotosAlbum({
  20.               filePath: newPath,
  21.               success: resolve,
  22.               fail: reject,
  23.             });
  24.           });
  25.           uni.showToast({
  26.             title: "保存成功",
  27.             icon: "success",
  28.           });
  29.           this.closeOptionsPopup();
  30.         } else {
  31.           uni.showToast({
  32.             title: "保存失败",
  33.             icon: "none",
  34.           });
  35.           throw new Error("下载失败");
  36.         }
  37.       } catch (err) {
  38.         console.error("操作失败", err);
  39.         uni.showToast({
  40.           title: "操作失败",
  41.           icon: "none",
  42.         });
  43.       }
  44.     },    /**
  45.      * 重命名文件,加上指定后缀
  46.      * @param {string} oldPath - 原文件路径
  47.      * @param {string} fileSuffix - 文件后缀,例如 '.jpg'
  48.      * @returns {Promise<string>} - 返回重命名后的文件路径字符串
  49.      */
  50.     renameImage(oldPath, fileSuffix) {
  51.       return new Promise((resolve, reject) => {
  52.         plus.io.resolveLocalFileSystemURL(
  53.           oldPath,
  54.           (entry) => {
  55.             entry.getParent(
  56.               (parentEntry) => {
  57.                 const newName = entry.name + fileSuffix;
  58.                 entry.moveTo(
  59.                   parentEntry,
  60.                   newName,
  61.                   (newFileEntry) => {
  62.                     resolve(newFileEntry.fullPath);
  63.                   },
  64.                   (err) => {
  65.                     console.error("文件移动失败", err);
  66.                     reject(`文件移动失败: ${err.message}`);
  67.                   }
  68.                 );
  69.               },
  70.               (err) => {
  71.                 console.error("获取父目录失败", err);
  72.                 reject(`获取父目录失败: ${err.message}`);
  73.               }
  74.             );
  75.           },
  76.           (err) => {
  77.             console.error("解析文件路径失败", err);
  78.             reject(`解析文件路径失败: ${err.message}`);
  79.           }
  80.         );
  81.       });
  82.     },    // 获取字典数据    async getDictsData() {      for (const dict of this.dicts) {        const response = await getDictsByCodeValue(dict.codeId);        this.dictMap.set(dict.codeName, response);      }      // 将字典名和对应数据的数组转换为对象      this.dictData = Object.fromEntries(Array.from(this.dictMap.entries()));    },    /**     * @dataList  数据聚集     * @original  源数据     * @matchKey  要匹配的key     * @originalKey 最终需要获取的key     */    findDataInfo(dataList, original, matchKey, originalKey) {      if (!dataList) {        return original;      }      const dataItem = dataList.find((item) => item[matchKey] == original);      return dataItem ? dataItem[originalKey] : original; // 如果找不到,则返回原始ID    },    openImagePopup() {      this.$refs.popup.open();    },    closeImagePopup() {      this.$refs.popup.close();    },    openOptionsPopup() {      this.$refs.optionsPopup.open();    },    closeOptionsPopup() {      this.$refs.optionsPopup.close();    },  },  watch: {},  async created() {    await this.getDictsData();    await this.getNotifyList();  },  // 页面周期函数--监听页面加载  onLoad() {},  // 页面周期函数--监听页面初次渲染完成  onReady() {},  // 页面周期函数--监听页面表现(not-nvue)  onShow() {},  // 页面周期函数--监听页面隐藏  onHide() {},  // 页面周期函数--监听页面卸载  onUnload() {},  // 页面处理函数--监听用户下拉动作  // onPullDownRefresh() { uni.stopPullDownRefresh(); },  // 页面处理函数--监听用户上拉触底  // onReachBottom() {},  // 页面处理函数--监听页面滚动(not-nvue)  // onPageScroll(event) {},  // 页面处理函数--用户点击右上角分享  // onShareAppMessage(options) {},};</script><style lang="scss">.notify {  padding-bottom: 10px;}.card-actions {  display: flex;  flex-direction: row;  justify-content: space-around;  align-items: center;  height: 45px;  border-top: 1px #eee solid;}.card-actions-item {  display: flex;  flex-direction: row;  align-items: center;}.card-actions-item-text {  font-size: 12px;  color: #666;  margin-left: 5px;}.no-border {  border-width: 0;}/* 弹出框样式 */.popup-container {  position: relative;  width: 100%;  height: 100%;  background-color: white;  border-radius: 10px;  overflow: hidden;  display: flex;  justify-content: center; /* 水平居中 */  align-items: center; /* 垂直居中 */}/* 图片包装样式 */.image-wrapper {  width: 100%;  height: 100%;  display: flex;  justify-content: center; /* 水平居中 */  align-items: center; /* 垂直居中 */}/* 预览图片样式 */.preview-image {  object-fit: contain;}/* 关闭按钮样式 */.close-button {  position: absolute;  top: 10px;  right: 10px;  background-color: transparent;  border: none;  font-size: 24px;  color: black;}/* 选项弹出框样式 */.options-container {  padding: 0;  background-color: #f5f5f5;  border-top-left-radius: 12px;  border-top-right-radius: 12px;  box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.15);  overflow: hidden; /* 确保圆角效果 */}/* 利用项样式 */.option-item {  padding: 15px;  font-size: 15px;  text-align: center;  background-color: white;}/* 分隔线样式 */.separator {  height: 1px;  background-color: #e5e5e5;}/* 隔断样式 */.gap {  height: 6px;  background-color: #f5f5f5;}/* 取消按钮样式 */.cancel-item {  padding: 15px;  font-size: 15px;  text-align: center;  background-color: white;}</style>
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

卖不甜枣

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表