uniapp h5、webview引入的h5、app里的下载 -

作者: 阿巳交不起水电费 | 来源:发表于2023-08-09 11:03 被阅读0次

    前言

    最近在写uniapp,第一次写。。。项目从pc端写个对应的app端。遇到下载功能,想到这功能比较常见,而且社区很多人遇到这个问题,就多写点,写通用点,后面再遇到也可以用,分享给大家,记得点赞哦!

    自行选择合适方法即可,支持:

    1.uniapp h5 端下载。
    2.unapp 开发的页面嵌入到uniapp webview 中,或者 h5 plus 中
    3.uniapp 非h5 端下载 (支持app,其他端自行测试)
    4. axios + responseType: "blob" 的下载
    5. uni.request + responseType: 'arraybuffer' + window.URL.createObjectURL(返回值) + a标签download方式 下载

    代码如下:

    /**
     * 文件下载相关支持axios、uniapp各端
     * author: yangfeng
     * date: 2023/08/10
     * /
    
    /**
     * pc端axios方式下载
     * 方式一:使用 uni.request + responseType: 'arraybuffer' +  window.URL.createObjectURL(返回值) + a标签download方式
     * 方式二:使用 axios + responseType: "blob"
     * @param {*} arrayBufferOrBlob arraybuffer 或者 blob。若是axios则设置为 responseType: 'blob',若使用 uni.request 则 responseType: 'arraybuffer'
     * @param {*} fileName 需要保存的文件名称
     */
    export function pc_axios_download(arrayBufferOrBlob, fileName) {
      const blob = new Blob([arrayBufferOrBlob], {
        type: "application/vnd.ms-execl",
      });
      if (typeof window.navigator.msSaveBlob !== "undefined") {
        // 兼容IE,window.navigator.msSaveBlob:以本地方式保存文件
        window.navigator.msSaveBlob(blob, decodeURI(fileName));
      } else {
        // 创建新的URL并指向File对象或者Blob对象的地址
        const blobURL = window.URL.createObjectURL(blob);
        // 创建a标签,用于跳转至下载链接
        const tempLink = document.createElement("a");
        tempLink.style.display = "none";
        tempLink.href = blobURL;
        tempLink.setAttribute("download", decodeURI(fileName));
        // 兼容:某些浏览器不支持HTML5的download属性
        if (typeof tempLink.download === "undefined") {
          tempLink.setAttribute("target", "_blank");
        }
        // 挂载a标签
        document.body.appendChild(tempLink);
        tempLink.click();
        document.body.removeChild(tempLink);
        // 释放blob URL地址
        window.URL.revokeObjectURL(blobURL);
      }
    }
    
    /**
     * h5方式下载临时路径的文件 - 创建a标签,download的方式
     * @param {*} blobURL 附件的临时路径
     * @param {*} fileName 下载后的该文件名称
     */
    export function h5_download(blobURL, fileName) {
      // 创建a标签,用于跳转至下载链接
      const tempLink = document.createElement("a");
      tempLink.style.display = "none";
      tempLink.href = blobURL;
      tempLink.setAttribute("download", decodeURI(fileName));
      // 兼容:某些浏览器不支持HTML5的download属性
      if (typeof tempLink.download === "undefined") {
        tempLink.setAttribute("target", "_blank");
      }
      // 挂载a标签
      document.body.appendChild(tempLink);
      tempLink.click();
      document.body.removeChild(tempLink);
      // 释放blob URL地址
      window.URL.revokeObjectURL(blobURL);
    }
    
    function checkDownload() {
      plus.io.requestFileSystem(plus.io.PUBLIC_DOWNLOADS, function (fs) {
        var directoryReader = fs.root.createReader();
        directoryReader.readEntries(
          function (entries) {
            var i;
            for (i = 0; i < entries.length; i++) {
              console.log(entries[i].name);
              entries[i].name = i;
            }
          },
          function (e) {
            console.log("Read entries failed: " + e.message);
          }
        );
      });
    }
    // h5Plus 下载 - plus.downloader.createDownload api
    export function h5Plus_download(url, options = {}) {
      return new Promise((resolve, reject) => {
        let dtask = plus.downloader.createDownload(
          url,
          options,
          function (d, status) {
            if (status == 200) {
              //下载成功,d.filename是文件在保存在本地的相对路径,使用下面的API可转为平台绝对路径
              let fileSaveUrl = plus.io.convertLocalFileSystemURL(d.filename);
              console.log(fileSaveUrl, "平台绝对路径");
              uni.showModal({
                title: "下载成功,是否打开文件?",
                content: "文件目录存储目录为:" + fileSaveUrl,
                success: function (res) {
                  if (res.confirm) {
                    // console.log('用户点击确定');
                    //选择软件打开文件
                    plus.runtime.openFile(d.filename);
                  } else if (res.cancel) {
                    // console.log('用户点击取消');
                  }
                },
              });
    
              resolve(d);
    
              // checkDownload()
              // console.log("Download success: " + JSON.stringify(d));
    
              // 保存图片、视频到相册中
              // plus.gallery.save(d.filename, function () {
              //   resolve(d);
              //   console.log( "保存图片到相册成功" );
              //   // 打开文件
              //   plus.runtime.openFile(d.filename, (err) => {
              //     console.log(err);
              //   });
              // },function(){
              //   reject();
              //   console.log( "保存图片到相册失败" );
              // });
    
              // let arr = fileSaveUrl.split('/')
              // let dirPath = arr.slice(0, arr.length-1).join('/') // 当前文件所在目录
    
              // console.log(getApp().globalData, dirPath, 666)
              // // 通过URL参数获取目录对象或文件对象
              // plus.io.resolveLocalFileSystemURL(dirPath, function( entry ) {
              //   console.log(entry, 44)
              //   // 打开文件目录
              //   entry.getDirectory('',{create:true,exclusive:false},function( dir ){
              //     console.log("Directory Entry Name: " + dir.name);
              //   }, function (e) {
              //     console.error(e, 444)
              //   })
    
              // }, function ( e ) {
              //   alert( "Resolve file URL failed: " + e.message );
              // } );
            } else {
              reject();
              console.log("Download failed: " + status);
            }
          }
        );
        dtask.start();
      });
    }
    
    // 非h5下载
    export function noH5_download(tempFilePath) {
      return new Promise((resolve, reject) => {
        uni.saveFile({
          tempFilePath: tempFilePath,
          success: function (red) {
            let savedFilePath = red.savedFilePath; // 相对路径
            let fileSaveUrl = plus.io.convertLocalFileSystemURL(savedFilePath);
            console.log(fileSaveUrl, "平台绝对路径");
            uni.showModal({
              title: "提示",
              content: "文件已保存:" + fileSaveUrl,
              cancelText: "我知道了",
              confirmText: "打开文件",
              success: function (resMdel) {
                if (resMdel.confirm) {
                  uni.openDocument({
                    filePath: savedFilePath,
                    success: (sus) => {
                      console.log("成功打开");
                    },
                    fail(){
                      uni.showToast({
                        icon: 'none',
                        title: '暂不支持打开此格式文件'
                      })
                    }
                  });
                }
              },
            });
            resolve(red);
          },
          fail(e) {
            console.error(e);
            reject(e);
          },
        });
      });
    }
    
    /**
     *下载文件资源到本地,客户端直接发起一个 HTTP GET 请求,返回文件的本地临时路径。注意:1.h5可能存在跨域问题 2.只支持get方式下载
     * @param {*} url 下载的完整路径
     * @param {*} fileName 下载后的该文件名称
     * @param {*} config uni.downloadFile的其他配置,如可以在header 添加token参数
     * @returns Promise<any>
     */
    export function uni_DownloadFile(url, fileName, config = {}) {
      return new Promise((resolve, reject) => {
        uni.downloadFile({
          url: url, //仅为示例,并非真实的资源
          // header: {
          //   "x-token": useUserStore().token,
          // },
          ...config,
          success: (res) => {
            let tempFilePath = res.tempFilePath;
            // console.log(res, 4444);
    
            // h5 方式下载
            // #ifdef H5
            let isPlus = null;
            try {
              isPlus = plus;
            } catch (e) {}
            if (isPlus) {
              // 是否支持h5 plus,针对当前页面被uniapp webview 引入 或者被包裹在 h5+ 壳子里,使用 h5plus 方式下载
              // 采用a标签download方式下载会失败,因此需要针对处理
              // 虽然会弹出下载框,但是下载之后你发现打开时失败,文件路径错误;这是因为webview中下载文件出现套娃现象
              console.log("当前是h5 嵌入到 h5+ 环境");
              h5Plus_download(url, { filename: "_downloads/" + fileName })
                .then((result) => {
                  resolve(result);
                })
                .catch(() => {
                  reject();
                });
            } else {
              console.log("当前是h5环境下载");
              // window.location.href= tempFilePath
              h5_download(tempFilePath, fileName);
              resolve(res);
            }
            // #endif
    
            // 非 h5 方式下载 - 需要保存文件到本地,如app里面的下载
            // #ifndef H5
            console.log("当前非h5环境下载");
            noH5_download(tempFilePath)
              .then((result) => {
                resolve(result);
              })
              .catch(() => {
                reject();
              });
            // #endif
          },
          fail(e) {
            console.error(e);
            reject(e);
          },
        });
      });
    }
    
    
    
    

    调用方式:

    1.uni 方式下载,兼容多端,如h5、嵌入到webview的h5、嵌入到h5 plus 环境的h5、uniapp vueapp等,其他的自行测试

    /**
     * 根据下载路径下载文件
     * @param {*} url 下载路径,绝对路径,必选
     * @param {*} fileName 下载文件名称,必选
     * @returns
     */
    export const downloadFileFromUrl = (url, fileName) => {
    
      // uni方式 下载
      return uni_DownloadFile(url,fileName,{
            header: {
            "x-token": useUserStore().token,
          },
      })
    
      // pc、h5 方式下载
      // return service
      // .request({
      //   url: url,
      //   method: "get",
      //   // responseType: "blob", // axios 等pc端通用下载
      //   responseType: 'arraybuffer', // uni.request方式下载 文档里说这里只有text、arraybuffer,因此不能使用blob
      //   headers: {
      //     "x-token": useUserStore().token,
      //   },
      // }, false)
      // .then((res) => {
      //   // console.log(res,res.data, 111)
      //   if(res.status !== 200) return Promise.reject(res.errMsg || res.message || '下载失败')
      //   pc_axios_download(
      //     res.data,
      //     fileName ||
      //       (res.headers || res.header)["content-disposition"].replace(
      //         /.+filename=(.+)$/,
      //         "$1"
      //       ) ||
      //       url.split("/").pop()
      //   );
      // })
      // .catch((err) => {
      //   console.log(err);
      //   return Promise.reject(err);
      // });
        
    };
    

    2. uni.request 方式在h5端下载

    /**
     * 根据下载路径下载文件
     * @param {*} url 下载路径,绝对路径,必选
     * @param {*} fileName 下载文件名称,必选
     * @returns
     */
    export const downloadFileFromUrl = (url, fileName) => {
    
      // uni方式 下载
      // return uni_DownloadFile(url,fileName,{
      //       header: {
      //       "x-token": useUserStore().token,
      //     },
      // })
    
      // pc、h5 方式下载
      return service
      .request({
        url: url,
        method: "get",
        // responseType: "blob", // axios 等pc端通用下载
        responseType: 'arraybuffer', // uni.request方式下载 文档里说这里只有text、arraybuffer,因此不能使用blob
        header: {
          "x-token": useUserStore().token,
        },
      }, false)
      .then((res) => {
        // console.log(res,res.data, 111)
        if(res.status !== 200) return Promise.reject(res.errMsg || res.message || '下载失败')
        pc_axios_download(
          res.data,
          fileName ||
            (res.headers || res.header)["content-disposition"].replace(
              /.+filename=(.+)$/,
              "$1"
            ) ||
            url.split("/").pop()
        );
      })
      .catch((err) => {
        console.log(err);
        return Promise.reject(err);
      });
        
    };
    

    3.axios 方式下载,这样pc端也可以用

    /**
     * 根据下载路径下载文件
     * @param {*} url 下载路径,绝对路径,必选
     * @param {*} fileName 下载文件名称,必选
     * @returns
     */
    export const downloadFileFromUrl = (url, fileName) => {
    
      // uni方式 下载
      // return uni_DownloadFile(url,fileName,{
      //       header: {
      //       "x-token": useUserStore().token,
      //     },
      // })
    
      // pc、h5 方式下载
      return service
      .request({
        url: url,
        method: "get",
        responseType: "blob", // axios 等pc端通用下载
        // responseType: 'arraybuffer', // uni.request方式下载 文档里说这里只有text、arraybuffer,因此不能使用blob
        headers: {
          "x-token": useUserStore().token,
        },
      }, false)
      .then((res) => {
        // console.log(res,res.data, 111)
        if(res.status !== 200) return Promise.reject(res.errMsg || res.message || '下载失败')
        pc_axios_download(
          res.data,
          fileName ||
            (res.headers || res.header)["content-disposition"].replace(
              /.+filename=(.+)$/,
              "$1"
            ) ||
            url.split("/").pop()
        );
      })
      .catch((err) => {
        console.log(err);
        return Promise.reject(err);
      });
        
    };
    

    注意:我这边自行将uni.request封装了下,调用方式向axios靠齐的,本来我这边调用没这么复杂,后面两种其实差不多。这里还是分开写的,避免有些同学搞不清楚。比如uni.request 是header不是headers,responseType只有arraybuffer没有blob等。

    总结:

    这边的项目目前采用webview引入uniapp开发的页面的方式,这样简单点,相当于iframe直接引入,很多pc端代码可以直接复用【这是个低代码平台,能少改点最好不过】【才不是因为直接打成app各种报错。。。】。后面发现嵌入到uniapp webview里面是没法使用a标签download下载这种方式的,虽然会弹出下载框但是会下载失败,浏览器上是正常下载的。因此采用的h5 plus 方式下载。

    注意:

    1.如果在h5 plus 下遇到下载失败,看下是否 plusready 完成

    image.png
    app 端可以直接使用h5+ api
    image.png

    2.若遇到这个报错: Refused to get unsafe header "content-disposition"

    image.png

    后端设置下resopnse content-disposition 应该就行了,不管他也没啥,不影响下载。

    若对你有帮助,请点个赞吧,谢谢支持!
    本文地址:https://www.jianshu.com/p/d827dd9ed17e?v=1691636612446,转载请注明出处,谢谢。

    参考:
    webview 引入h5页面的下载
    分区存储

    相关文章

      网友评论

        本文标题:uniapp h5、webview引入的h5、app里的下载 -

        本文链接:https://www.haomeiwen.com/subject/deiopdtx.html