安卓端微信H5下载文件处理:让微信自动弹起跳转外部浏览器窗口

这篇具有很好参考价值的文章主要介绍了安卓端微信H5下载文件处理:让微信自动弹起跳转外部浏览器窗口。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

配套视频:https://www.bilibili.com/video/BV1oA411B7gv/


背景

今天鼓捣了一下手机投屏到笔记本,就想录个视频展示一下学习成果,正好就想起了很早之前实现的这个功能。
H5文件下载是一个很简单的功能,但是把这个H5放在安卓版微信打开,功能就不能用了,因为安卓端的微信内置浏览器拦截了所有下载文件的请求。
即使微信的sdk也没有提供直接保存文件的接口,所以出路只有一条,就是跳到第三方应用进行下载,比如跳到手机浏览器、跳到微信小程序。如果是上架了应用宝的app,可以跳转应用宝下载。
之所以屏蔽,应该是H5无法监管的原因,但是不能理解的是,ios端的微信是可以下载的,难道苹果手机高人一等?

解决方案收集

  • 1.微信公众号sdk(无法实现)

    • 可能以前有这个功能,但是现在确实是没有了,找不到这种接口
    • 微信公众号sdk官方文档:附录2-所有 JS 接口列表
  • 2.跳转第三方应用

    • 2.1.跳转小程序(没有实践过,但是跳转小程序,还不如跳转手机浏览器呢)
      • 参考:在微信浏览器打开 H5,居然无法一键下载图片?
    • 2.2.第三方应用生成的链接可以直接触发跳转外部浏览器选择窗口(骗人的吧)
      • 参考:成功解决微信跳转到手机默认浏览器下载

      • 这些网站都打不开了,不靠谱

    • 2.3.如果是app,可以跳腾讯出品的应用宝下载
      • 参考:H5在微信下载app
    • 2.4.前端写弹窗提示或是遮罩提示,引导用户在右上角通过浏览器打开
      • 参考:2022-12-06 uniApp H5端实现下载文件(包含微信浏览器内处理)
      • 参考:微信H5保存或下载视频到本地,将视频直接分享视频给好友
      • 参考:微信跳转手机默认浏览器提示 微信h5页面中下载第三方app的方法

最终选择的解决方案

  • 想到这个方案,是一个意外。

  • 一开始我只测试了zip的下载,确实不能下载,以至于我以偏概全地以为所有格式都不能下载,所以就转到百度上找答案。

    安卓端微信H5下载文件处理:让微信自动弹起跳转外部浏览器窗口
  • 然后测试跟我说,ios的文件有些也不能预览,不能下载。

  • 所以我就丢下这个坑,先去解决ios的问题。

  • 百度发现ios也是伪下载,它是先以预览的方式打开文件,需要用户点击右上角手动保存。

  • 而且文件后缀和响应头 content-type要严格对应,不对应就会报错,预览不了

  • 参考:解决移动端H5下载文件提示文件类型无法识别或非法文件的问题

  • 改完ios的问题,我传了各种格式的文件测试了一遍,确认修复之后,又转回安卓端。

  • 随手点击了几下,就是这么几下让我看到了希望。

    安卓端微信H5下载文件处理:让微信自动弹起跳转外部浏览器窗口
  • 并不是所有类型的文件都不能下载,针对docx、pdf、xlsx、txt等格式,微信会主动唤起跳转其他浏览器的选择弹窗。

  • 这比起前端写提示窗无疑要友好许多。

  • 所以只要发挥偷蒙拐骗的优良品质,让微信对所有文件一视同仁,都唤起跳转窗口就行了。

  • 到此,安卓端H5下载文件的问题完美解决。

  • 欺骗的手段也很简单,反正微信也不能下载,就所有的下载请求,都给它一个假文件,比如123456.xlsx。

java实现

  • 注意,如果接口使用cookie鉴权,跳转外部浏览器,cookie是带不过去的。
  • 需要提供一个不需要鉴权的接口,换一种方式鉴权,比如时效分享码或者直接携带sessionId之类的
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
public class ApiController {

    // 获取日志对象 Spring Boot 中内置了日志框架 Slf4j
    private static Logger log = LoggerFactory.getLogger(ApiController.class);

    /**
     * 处理微信文件下载
     * 欺骗安卓微信唤起打开外部浏览器的选择框
     * ios微信可以预览每种格式的文件,但是不支持直接下载,需要用户在预览页点右上角手动保存
     * 另外,ios对content_type要求严格,如果文件后缀和content_type对不上,连预览页都进不了
     * 企业微信不用做任何处理
     */
    @GetMapping("downloadFileWx")
    public void downloadFileWx(@RequestParam String path, HttpServletRequest request, HttpServletResponse response) throws Exception {
        responseOutputFileWx(path, null, request, response);
    }

    /**
     * 响应文件流
     * @param path 文件路径
     * @param outputFileName 文件名称,赋值给响应头Content-Disposition
     * @param request
     * @param response
     */
    public void responseOutputFileWx(String path, String outputFileName,
                                   HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        File file = new File(path);
        if (file == null || !file.exists() || !file.isFile()) {
            log.error("文件不存在");
            // 重定向到当前页面,相当于刷新页面
            String contextPath = request.getContextPath();
            response.sendRedirect(contextPath + "/downFile");
            return;
        }

        if (outputFileName == null || outputFileName.trim().length() == 0) {
            // 假如下载文件名参数为空,则设置为原始文件名
            outputFileName = file.getName();
        }
        ServletContext context = request.getServletContext();
        // 文件绝对路径
        String absolutePath = file.getAbsolutePath();
        // 获取文件的MIME type
        String mimeType = context.getMimeType(absolutePath);
        if (mimeType == null) {
            // 没有发现则设为二进制流
            mimeType = "application/octet-stream";
        }

        response.setContentType(mimeType);
        // 设置文件下载响应头
        String headerKey = "Content-Disposition";
        String headerValue = null;

        if (isWx(request)) {
            // 微信浏览器,打开手机默认浏览器下载文件
            // 注意排除企业微信
            try {
                if (isAndroidWx(request)) {
                    // 安卓端,xlsx文件类型会触发微信弹出跳转外部浏览器窗口,欺骗一下
                    response.setContentType("application/octet-stream");
                    outputFileName = "123456.xlsx";
                } else {
                    // ios 微信对contentType要求比较严格
                    // https://juejin.cn/post/6844904086463053837
                    if (absolutePath.endsWith("xlsx")) {
                        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
                    } else if (absolutePath.endsWith("xls")) {
                        response.setContentType("application/vnd.ms-excel");
                    } else if (absolutePath.endsWith("doc")) {
                        response.setContentType("application/msword");
                    } else if (absolutePath.endsWith("docx")) {
                        response.setContentType("application/application/vnd.openxmlformats-officedocument.wordprocessingml.document");
                    }
                }
                headerValue = String.format("attachment; filename=\"%s\"", URLEncoder.encode(outputFileName, "UTF-8"));
            } catch (Exception e) {
                headerValue = String.format("attachment; filename=\"%s\"", outputFileName);
                log.error(e.getMessage(), e);
            }
        } else {
            try {
                // 解决Firefox浏览器中文件名中文乱码
                // https://blog.csdn.net/Jon_Smoke/article/details/53699400
                headerValue = String.format("attachment; filename* = UTF-8''%s",
                        URLEncoder.encode(outputFileName, "UTF-8")
                );
            } catch (Exception e) {
                headerValue = String.format("attachment; filename=\"%s\"", outputFileName);
                log.error(e.getMessage(), e);
            }
        }
        response.setHeader(headerKey, headerValue);

        String fileName = file.getName();
        try (OutputStream outputStream = response.getOutputStream()) {
            response.setCharacterEncoding("utf-8");

            // 将下面2行放开,可以测试微信最原始反应
            // 设置返回类型
            // response.setContentType("multipart/form-data");
            // // 文件名转码一下,不然会出现中文乱码
            // response.setHeader("Content-Disposition", "attachment;fileName=" + encodeStr(fileName));

            byte[] bytes = readBytes(file);
            if (bytes == null) {
                log.error("文件不存在");
            }
            outputStream.write(bytes);
            log.info("文件下载成功!" + fileName);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 对字符串(文件名或路径)进行url编码
     */
    private String encodeStr(String str) throws Exception {
        return URLEncoder.encode(str, "UTF-8");
    }

    /**
     * 将文件转为byte数组
     */
    public byte[] readBytes(File file) throws Exception {
        long len = file.length();
        // 无论数组的类型如何,数组中的最大元素数为Integer.MAX_VALUE,大约20亿
        if (len >= 2147483647L) {
            return null;
        } else {
            byte[] bytes = new byte[(int) len];

            try (FileInputStream in = new FileInputStream(file)) {
                int readLength = in.read(bytes);
                if ((long) readLength < len) {
                    log.error("文件未读取完全");
                    return null;
                }
            } catch (Exception var10) {
                return null;
            }

            return bytes;
        }
    }

    /**
     * 是否从安卓端微信请求,需要排除企业微信
     */
    private static boolean isAndroidWx(HttpServletRequest request) {
        String userAgent = request.getHeader("user-agent");
        return userAgent != null && userAgent.toLowerCase().indexOf("micromessenger") > -1
                && userAgent.toLowerCase().indexOf("wxwork") < 0
                && userAgent.toLowerCase().indexOf("android") > -1;
    }

    /**
     * 是否从微信请求,需要排除企业微信
     * 安卓或ios
     */
    private static boolean isWx(HttpServletRequest request) {
        String userAgent = request.getHeader("user-agent");
        return userAgent != null && userAgent.toLowerCase().indexOf("micromessenger") > -1
                && userAgent.toLowerCase().indexOf("wxwork") < 0;
    }

}

题外话:手机如何投屏笔记本

方式1:win10自带投屏

  • 按 “Windows 徽标键+I” 打开设置,设置–>系统–>投影到此电脑

  • 投影到此电脑中显示灰色不可选,或显示“我们正在确认这项功能”

  • 第一次需要安装 无线显示器

  • 手机使用电脑自带功能进行投屏

  • 手机投屏到笔记本之后,笔记本会被劫持,就是只能操作手机画面,鼠标移不出来,可以在电脑上用鼠标直接操作手机。

  • 这一点,有点不方便,比如想一边写代码,一边预览手机效果,就不能实现。

  • 另外,建议选择 仅第一次 需要验证,我第一次投成功了,关闭之后,就死活投不上去,主要是笔记本不能弹出确认对话框

  • 之后,重启电脑才能第二次投屏成功。文章来源地址https://www.toymoban.com/news/detail-403217.html

方式2:幕享 软件

  • 官网下载页
  • 官方使用教程:如何使用幕享Windows版
  • 我是在这个分享视频里面找到的这个软件:需要手机投屏电脑?这五款软件就够了!
  • 这是纯投屏软件,不能在笔记本上操作手机。对手机录屏,然后传输到笔记本上,局域网下延迟不高。

到了这里,关于安卓端微信H5下载文件处理:让微信自动弹起跳转外部浏览器窗口的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 让微信公众号自动回复可以跳转小程序的消息

    1、在公众号中关联对应的小程序 点击公众号左侧菜单栏“小程序管理”,点击“添加”按钮。 ​ 添加小程序,点击“关联小程序” ​ 输入对应的小程序名称,点击搜索,显示出对应的小程序信息,点击“下一步”按钮,用管理员的微信扫码授权即可。 ​ 2、设置公众号的

    2024年02月12日
    浏览(36)
  • 微信H5下载文件、微信浏览器无法下载文件解决方案

    手机端的微信访问网页的时候,是禁止直接下载文件的 但是IOS端可以预览.txt/.doc/.docx/.xls/xlsx/.pdf等格式的文件,Android端在下载这些格式的文件时,可以唤起 ‘即将离开微信,在浏览器打开’ 提示 所以,根据手机微信的这些限制,考虑在下载文件时,使Android微信在下载任何

    2024年02月16日
    浏览(66)
  • 微信浏览器H5下载文件

    微信浏览器无法下载文件,我们可以跳转外部浏览器进行下载。 首先绑定按钮事件(我这里用的uniapp开发) button class=“btn” @click=“downFile”点击下载 然后判断当前页面是否是微信浏览器,若是,提示用户打开右上角选择浏览器打开链接,若不是,进行下载 进入默认浏览器

    2024年02月16日
    浏览(53)
  • uniapp:h5和微信小程序文件下载方式

    一、h5浏览器端下载方式,直接使用a标签 download属性指定下载文件的文件名,也可以不加 注意:记得一定要加ifdef注释,不然其他端也会显示a标签 二、微信小程序下载方式,通过uniapp的downloadFile和wx小程序的saveFile保存文件 wx保存文件的api只是临时保存图片文件,可以通过微

    2024年02月07日
    浏览(82)
  • uniapp实现H5、APP、微信小程序三端文件下载

    这里我使用了uniapp官方api uni.downloadFile 和 uni.openDocument APP使用了uniapp官方api uni.downloadFile 和 uni.saveImageToPhotosAlbum(OBJECT) 还有 uni.openDocument H5的方法比较简单可以直接使用window.open方法下载。即: 如果你的浏览器支持预览,就会自动打开预览文件,然后自己手动下载文件,不支

    2024年02月16日
    浏览(34)
  • 实现微信小程序web-view内嵌H5中的下载功能(大文件切片下载)

    微信小程序的开发框架是uniapp,使用uniapp脚手架搭建,其中有页面是展示另一个小程序,在这个页面主体内容使用了标签将H5的页面内容展示,H5中有页面存放了下载的路径。点击下载按钮下载文件,或者预览文件让用户手动保存。 如果是pc端,下载用一个 a 标签就很容易,但

    2024年02月10日
    浏览(87)
  • uniapp实现h5、app、微信小程序三端pdf文件下载和预览

    以下代码兼容三端,app,h5,微信小程序,经过个人测试 手机端有两种方法,使用renderjs或者uniapp的api 两者的区别 使用renderjs的写法,会提示用户是否下载文件,下载完成后用户需要手动点击下载的文件,才会打开文件 使用uniapp的api则可以直接下载并直接预览,不需要用户操

    2024年02月11日
    浏览(31)
  • 项目笔记——安卓WebView加载H5页面问题处理

    项目为Android应用,使用WebView加载H5页面。 此文仅记录项目开发中遇到的问题及解决方法。 目录 一,下拉刷新 二,H5唤起支付宝 三,H5本地文件选择 四,加载图片失败 五,输入框被软键盘遮挡 页面Reload需要下拉刷新功能,所以使用了SwipeRefreshLayout包裹WebView。但使用时不管

    2024年02月02日
    浏览(33)
  • uniapp - 详细手机端h5网页调用手机摄像头进行二维码识别扫描,在uniapp的H5移动端微信网页平台中,在浏览器中调用手机扫码功能详细教程,识别二维码并获取扫描结果(完整示例源码,一键复制即用

    正常情况下,使用uniapp扫码API是不行的,因为不支持h5端。 在uniapp h5移动端网页项目中,实现了浏览器中调用手机摄像头扫码功能,uniapp手机网页H5扫描二维码功能实现,uni-app h5端调用摄像头扫码,提供完整可运行的代码。 跟着本文的步骤,复制源代码后运行改下就行了。

    2024年02月04日
    浏览(37)
  • 微信小程序文件上传、下载和图片处理、文件操作API的使用

    这次按照我的理解来做这部分的笔记 首先,复习上节课所学的内容。就是网络请求api的使用  现在我有一个需求就是点击按钮实现获取后端返回的图片  先打开服务器  看一下我们要返回的图片路径  书写结构  看一下返回来的数据。是在data下的banners里。因此我们封装一下

    2024年02月04日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包