• 有这样一个需求,一键发圈
    • 将页面的图片视频下载保存到相册
    • 将页面的文字复制到粘贴板
  • 我们的项目是小程序嵌套H5的老项目(虽然有点恶心,但是还得做)

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

  • 这是最开始的方式,h5调用微信api就是JSSDK了,里面有一个wx.downloadImage可以下载图片

  • 通过请求后台获注入JS-SDK配置信息 ```javascript var initWeixin = function(mpid){ var xtD = new Date(); var wxTtimestamp = xtD.getTime(); var wxNonceStr = “WXLMD”+wxTtimestamp; var url = location.href.split(‘#’)[0]; var wxSignature = null; $.ajax({
    1. method: "GET",
    2. url: "/wx/jsign",
    3. data: {"timestamp":wxTtimestamp,"noncestr":wxNonceStr,"url":url},
    4. dataType:'json',
    5. success:function(data){
    6. if(data.code!=0){
    7. layer.msg(data.message);
    8. return;
    9. }
    10. wxSignature = data.data.sign;
    11. wx.config({
    12. debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
    13. appId: data.data.app, // 必填,公众号的唯一标识
    14. timestamp: wxTtimestamp, // 必填,生成签名的时间戳
    15. nonceStr: wxNonceStr, // 必填,生成签名的随机串
    16. signature: wxSignature,// 必填,签名,见附录1
    17. jsApiList: ['downloadImage'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
    18. });
    19. },
    20. error:function () {
    21. layer.msg("网络繁忙");
    22. }
    }); }

wx.error(function(res){ // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。 console.log(res) });

initWeixin();

  1. - 配置成功就可以使用JS-SDKapi,调用下载图片的方法
  2. ```javascript
  3. wx.downloadImage({
  4. serverId: res.media_id, // 需要下载的图片的服务器端ID,由uploadImage接口获得
  5. isShowProgressTips: 1, // 默认为1,显示进度提示
  6. success: function (result) {
  7. var localId = result.localId; // 返回图片下载后的本地ID
  8. if (isEnd) {
  9. layer.msg('下载成功');
  10. }
  11. }
  12. });
  • media_id是通过发送图片的url请求后台,
  • 后台使用新增临时素材api上传图片到微信服务器,获取临时素材media_id
  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. // --------------上传素材文件至微信服务器--------------------
  65. public 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. }
  • 将media_id返回给前台拿到的

出现的问题

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

  • 本以为这样就可以成功了,

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

  • 找了下微信的目录路径,在文件夹\Tencent\MicroMsg\WeiXin

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

  • 这是下载一张图片的情况,下载多张也是会重复

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

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

  • 上面的方法遇到的问题没有得到解决,后来又让我们公司的一位大佬看了下这个功能需求
  1. 将一键下载图片功能的页面写在小程序里
  2. 在H5跳转到到详情时,使用wx.miniProgram.navigateTo跳转回小程序页面,并指向详情页面
  1. wx.miniProgram.navigateTo({
  2. url: '/pages/user/sourceMaterial/details/index?id=' + id
  3. });
  • 传递的id值是详情页面获取详情数据需要的接口
  1. 调用获取详情接口,渲染详情页面
  2. 点击一键发圈,调用小程序下载文件apiwx.downloadFile
  1. getDownloadFile: 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下载文件功能之后,要将图片保存到相册就需要先获取相册权限
  1. wx.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. 授权成功,保存图片成功的同时将文本复制到粘贴板
  1. clipboardData: 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. 功能完成,可以正常使用,并且可以下载视频