方法一:JS-SDK(不要用,不好用这个)

 1var initWeixin = function(mpid){
 2    var xtD = new Date();
 3    var wxTtimestamp = xtD.getTime();
 4    var wxNonceStr = "WXLMD"+wxTtimestamp;
 5    var url = location.href.split('#')[0];
 6    var wxSignature = null;
 7    $.ajax({
 8        method: "GET",
 9        url: "/wx/jsign",
10        data: {"timestamp":wxTtimestamp,"noncestr":wxNonceStr,"url":url},
11        dataType:'json',
12        success:function(data){
13            if(data.code!=0){
14                layer.msg(data.message);
15                return;
16            }
17            wxSignature = data.data.sign;
18            wx.config({
19                debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
20                appId: data.data.app, // 必填,公众号的唯一标识
21                timestamp: wxTtimestamp, // 必填,生成签名的时间戳
22                nonceStr: wxNonceStr, // 必填,生成签名的随机串
23                signature: wxSignature,// 必填,签名,见附录1
24                jsApiList: ['downloadImage'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
25            });
26        },
27        error:function () {
28            layer.msg("网络繁忙");
29        }
30    });
31}
32
33wx.error(function(res){
34    // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
35    console.log(res)
36});
37
38initWeixin();
 1wx.downloadImage({
 2    serverId: res.media_id, // 需要下载的图片的服务器端ID,由uploadImage接口获得
 3    isShowProgressTips: 1, // 默认为1,显示进度提示
 4    success: function (result) {
 5        var localId = result.localId; // 返回图片下载后的本地ID
 6        if (isEnd) {
 7            layer.msg('下载成功');
 8        }
 9    }
10});


  1@RequestMapping("/uploadImgToWx")
  2    public HashMap uploadImgToWx(HttpServletRequest req, HttpServletResponse response, String url,String suffix) throws IOException, Exception {
  3        // 验证是图片或视频
  4        String type = "image";
  5        if (suffix.equals("mp4")) {
  6            type = "video";
  7        }
  8        ByteArrayOutputStream outStream =new ByteArrayOutputStream();
  9        BufferedOutputStream stream =null;
 10        InputStream inputStream =null;
 11        File file =null;
 12        try {
 13            URL imageUrl =new URL(url);
 14            HttpURLConnection conn =(HttpURLConnection)imageUrl.openConnection();
 15            conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
 16            inputStream = conn.getInputStream();
 17            byte[] buffer =new byte[1024];
 18            int len =0;
 19            while( (len=inputStream.read(buffer)) != -1 ){
 20                outStream.write(buffer, 0, len);
 21            }
 22            file = File.createTempFile("pattern", "." + suffix);
 23            logger.info("临时文件创建成功={}", file.getCanonicalPath());
 24            FileOutputStream fileOutputStream =new FileOutputStream(file);
 25            stream =new BufferedOutputStream(fileOutputStream);
 26            stream.write(outStream.toByteArray());
 27        } catch (Exception e) {
 28            logger.error("创建服务器图片异常", e);
 29        } finally {
 30            try {
 31                if (inputStream !=null) inputStream.close();
 32                if (stream !=null) stream.close();
 33                outStream.close();
 34            } catch (Exception e) {
 35                logger.error("关闭流异常", e);
 36            }
 37        }
 38        MkWxUser user = RequestUtils.GetLoginUser(req);
 39        MkMpInfo info = mpInfoService.getMpInfo(user.getCeoId());
 40        String accessToken = "";
 41        // 获取 TOKEN
 42        if(info.getAuthorizerToken()!=null&&!"".equals(info.getAuthorizerToken())){
 43            accessToken = JedisUtil.get("authorToken_"+info.getUserId());
 44            if(accessToken==null){
 45                ApiAuthorizerTokenRet tokenRet = RefreshTockenUtil.refreshTocken(info.getAppid(),info.getRefreshToken(),info.getUserId());
 46                accessToken = tokenRet.getAuthorizer_access_token();
 47                info.setAuthorizerToken(tokenRet.getAuthorizer_access_token());
 48                info.setRefreshToken(tokenRet.getAuthorizer_refresh_token());
 49                mpInfoService.saveMpInfo(info,info.getUserId());
 50            }
 51        }else{
 52            accessToken = WxUtils.getAccessToken(info.getAppid(), info.getAppsecret());
 53        }
 54        // 新增临时素材 addTemImgUrl("https://api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN")
 55        String uploadUrl = WxApiConfig.addTemImgUrl.getValue().replace("ACCESS_TOKEN", accessToken);
 56        String json = HttpTool.uploadMedia(uploadUrl + "&type="+type, file, BaseConfig.encoding, 5000, 3000);
 57        ApiResult matterResult = new ApiResult(json);
 58        HashMap map = new HashMap();
 59        map.put("type", matterResult.getStr("type"));
 60        map.put("media_id", matterResult.getStr("media_id"));
 61        map.put("created_at", matterResult.get("created_at"));
 62        return map;
 63    }
 64// --------------上传素材文件至微信服务器--------------------
 65public static String uploadMedia( String url,File file,String charset,int connectTimeout,int responseTimeout ) throws Exception {
 66        HttpsURLConnection conn = null;
 67        InputStream in = null;
 68        OutputStream out = null;
 69        try{
 70            conn = (HttpsURLConnection)new URL( url ).openConnection();
 71            conn.setHostnameVerifier( hostnameVerifier );
 72            conn.setSSLSocketFactory( sslContext.getSocketFactory() );
 73            conn.setRequestMethod( METHOD_POST );
 74            conn.setDoInput( true );
 75            conn.setDoOutput( true );
 76            conn.setConnectTimeout( connectTimeout );
 77            conn.setReadTimeout( responseTimeout );
 78            // 定义数据分隔线
 79            String BOUNDARY = "----WebKitFormBoundaryiDGnV9zdZA1eM1yL"; 
 80            conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
 81            out = conn.getOutputStream();
 82            StringBuilder mediaData = new StringBuilder();
 83            mediaData.append("--").append(BOUNDARY).append("\r\n");
 84            mediaData.append("Content-Disposition: form-data;name=\"media\";filename=\""+ file.getName() + "\"\r\n");
 85            mediaData.append("Content-Type:application/octet-stream\r\n\r\n");
 86            byte[] mediaDatas = mediaData.toString().getBytes();
 87            out.write(mediaDatas);
 88            out.flush();
 89            DataInputStream fs = new DataInputStream(new FileInputStream(file));
 90            int bytes = 0;  
 91            byte[] bufferOut = new byte[1024];
 92            while ((bytes = fs.read(bufferOut)) != -1) {
 93                out.write(bufferOut, 0, bytes);
 94            }
 95            fs.close();
 96            out.write("\r\n".getBytes());
 97            byte[] end_data = ("\r\n--" + BOUNDARY + "--\r\n").getBytes();
 98            out.write(end_data);
 99            out.flush();
100            out.close();
101            // 定义BufferedReader输入流来读取URL的响应  
102            InputStream ins = conn.getInputStream();
103            BufferedReader read = new BufferedReader(new InputStreamReader(ins, "UTF-8"));
104            String valueString = null;
105            StringBuffer bufferRes = null;
106            bufferRes = new StringBuffer();
107            while ((valueString = read.readLine()) != null){
108                bufferRes.append(valueString);
109            }
110            ins.close();
111            return bufferRes.toString();
112        }finally{
113            if( conn!=null ){
114                conn.disconnect();
115            }
116            if( out!=null ){
117                try{
118                    out.close();
119                }catch( IOException e ){
120                    log.error( e );
121                }
122            }
123            if( in!=null ){
124                try{
125                    in.close();
126                }catch( IOException e ){
127                    log.error( e );
128                }
129            }
130        }
131    }



出现的问题

这个问题我在小程序社区也有问,但是没有答案,链接地址

1. 使用手机测试,下载图片成功,但是在手机相册找不到

2. 图片是有的,但是是重复的两张图片(一张原图,一张压缩过的)

在这两个问题没有的到解决

方法2:h5跳转小程序,使用小程序的原生下载文件api

  1. 将一键下载图片功能的页面写在小程序里
  2. 在H5跳转到到详情时,使用wx.miniProgram.navigateTo跳转回小程序页面,并指向详情页面


1wx.miniProgram.navigateTo({
2    url: '/pages/user/sourceMaterial/details/index?id=' + id
3});
  1. 调用获取详情接口,渲染详情页面
  2. 点击一键发圈,调用小程序下载文件apiwx.downloadFile


  1getDownloadFile: function (e) {
  2    let type = e.currentTarget.dataset.type
  3    wx.showLoading({
  4        title: '下载中',
  5        mask: true
  6    })
  7    let srcData = that.data.srcData
  8    let srcDataLen = srcData.length
  9    let successNum = 0
 10    for (let i = 0; i < srcDataLen; i++) {
 11        let imgUrl = srcData[i].replace('http://st', 'https://st')
 12        wx.downloadFile({
 13            url: imgUrl,
 14            success: function (res) {
 15                if (type == 1) {
 16                    wx.saveImageToPhotosAlbum({
 17                        filePath: res.tempFilePath,
 18                        success: function (data) {
 19                            successNum++
 20                            if (data.errMsg === 'saveImageToPhotosAlbum:ok') {
 21                                if (successNum == srcDataLen) {
 22                                    that.clipboardData()
 23                                }
 24                            }
 25                        },
 26                        fail: function (err) {
 27                            console.log(err)
 28                            wx.hideLoading()
 29                            if (err.errMsg === 'saveImageToPhotosAlbum:fail auth deny') {
 30                                wx.showToast({
 31                                    title: '未开启相册功能授权',
 32                                    icon: 'none',
 33                                    duration: 1500
 34                                })
 35                                wx.openSetting({
 36                                    success(settingdata) {
 37                                        if (settingdata.authSetting['scope.writePhotosAlbum']) {
 38                                            that.getDownloadFile()
 39                                        } else {
 40                                            wx.showToast({
 41                                                title: '授权失败',
 42                                                icon: 'none',
 43                                                duration: 1500,
 44                                            })
 45                                        }
 46                                    }
 47                                })
 48                            }
 49                            if (err.errMsg === 'saveImageToPhotosAlbum:fail cancel') {
 50                                wx.showToast({
 51                                    title: '已取消保存',
 52                                    icon: 'none',
 53                                    duration: 1500,
 54                                })
 55                            }
 56                        }
 57                    })
 58                } else {
 59                    wx.saveVideoToPhotosAlbum({
 60                        filePath: res.tempFilePath,
 61                        success: function (data) {
 62                            successNum++
 63                            if (data.errMsg === 'saveVideoToPhotosAlbum:ok') {
 64                                if (successNum == srcDataLen) {
 65                                    that.clipboardData()
 66                                }
 67                            }
 68                        },
 69                        fail: function (err) {
 70                            wx.hideLoading()
 71                            if (err.errMsg === 'saveVideoToPhotosAlbum:fail auth deny') {
 72                                wx.showToast({
 73                                    title: '未开启相册功能授权',
 74                                    icon: 'none',
 75                                    duration: 1500
 76                                })
 77                                wx.openSetting({
 78                                    success(settingdata) {
 79                                        if (settingdata.authSetting['scope.writePhotosAlbum']) {
 80                                            that.getDownloadFile()
 81                                        } else {
 82                                            wx.showToast({
 83                                                title: '授权失败',
 84                                                icon: 'none',
 85                                                duration: 1500,
 86                                            })
 87                                        }
 88                                    }
 89                                })
 90                            }
 91                            if (err.errMsg === 'saveVideoToPhotosAlbum:fail cancel') {
 92                                wx.showToast({
 93                                    title: '已取消保存',
 94                                    icon: 'none',
 95                                    duration: 1500,
 96                                })
 97                            }
 98                        }
 99                    })
100                }
101            }
102        })
103    }
104},

代码写的有点长,但是不影响功能

  1. 调用wx.downloadFile下载文件功能之后,要将图片保存到相册就需要先获取相册权限


 1wx.saveImageToPhotosAlbum({
 2      filePath: res.tempFilePath,
 3      success: function (data) {
 4          successNum++
 5          if (data.errMsg === 'saveImageToPhotosAlbum:ok') {
 6              if (successNum == srcDataLen) {
 7                  that.clipboardData()
 8              }
 9          }
10      },
11      fail: function (err) {
12          console.log(err)
13          wx.hideLoading()
14          if (err.errMsg === 'saveImageToPhotosAlbum:fail auth deny') {
15              wx.showToast({
16                  title: '未开启相册功能授权',
17                  icon: 'none',
18                  duration: 1500
19              })
20              wx.openSetting({
21                  success(settingdata) {
22                      if (settingdata.authSetting['scope.writePhotosAlbum']) {
23                          that.getDownloadFile()
24                      } else {
25                          wx.showToast({
26                              title: '授权失败',
27                              icon: 'none',
28                              duration: 1500,
29                          })
30                      }
31                  }
32              })
33          }
  1. 授权成功,保存图片成功的同时将文本复制到粘贴板


 1clipboardData: function () {
 2    wx.setClipboardData({
 3        data: that.data.resData.title + '\n' + that.data.resData.body,
 4        success(res) {
 5            wx.hideLoading()
 6            wx.showToast({
 7                title: '已复制文案和保存素材,快去分享给朋友吧。',
 8                icon: 'none',
 9                duration: 2000,
10                mask: true
11            })
12        },
13        fail: function (err) {
14            wx.showToast({
15                title: '内容复制出错',
16                icon: 'none',
17                duration: 1500
18            })
19        }
20    })
21}
  1. 功能完成,可以正常使用,并且可以下载视频