浏览器:Chrome

H5的audio标签,在使用过程中,出现有些情况出现总时长,有些时候没有总时长的情况,并且拖动条也不可拖动(自定义资源接口情况)。

有总时长:src是本地文件时,及使用云存储url地址时,显示总时长。

<div id="audioDiv">
	<audio id="aud" controls preload>
		<source src="a.wav" type="audio/wav">
	</audio>
</div>

无总时长:src地址是后台服务器读取音频文件后,回写的数据流并且响应头不包含Content-Length

<div id="audioDiv">
	<audio id="aud" controls preload>
		<source src="http://localhost:12386/audio" type="audio/wav">
	</audio>
</div>

后台服务器代码:audio标签获取不到总时长的缘故是当后台写回数据流,响应头没有Content-Length时,无法获取到总时长,只有加上该响应头,audio标签才能正确的获取总时长。

    @RequestMapping("audio")
    public void audio(HttpServletResponse response) throws IOException {
        File f = new ClassPathResource("a.wav").getFile();
        FileInputStream fis = new FileInputStream(f);
        // 无所谓,application/octet-stream也可以,不写也可以
        response.setHeader("Content-Type", "audio/wav");
        // 关键所在,如果后台直接读取文件流写回客户端,无Content-Length,则audio标签无法获取总时长
        response.setHeader("Content-Length", f.length() + "");
        byte[] buf = new byte[4096];
        int len;
        while ((len = fis.read(buf)) != -1) {
            response.getOutputStream().write(buf, 0, len);
        }
        response.getOutputStream().flush();
    }

拖动条不能拖动问题:后端需要继续增加响应头 Content-Range, Accept-Ranges


    @GetMapping("audio/{fileName}")
    public void audio(HttpServletResponse response, HttpServletRequest request, @PathVariable("fileName") String fileName) throws IOException {
        File f = new ClassPathResource(fileName).getFile();
        FileInputStream fis = new FileInputStream(f);

        // audio / video 标签拖动时发送的请求, 第一次:bytes=0-, 后续:bytes=startPos-endPos
        String rangeString = request.getHeader("Range");
        // range 值
        long rangeStart = Long.parseLong(rangeString.substring(rangeString.indexOf("=") + 1, rangeString.indexOf("-")));
        // range范围是从0个字节开始,所以结束位置下标是文件长度-1
        long rangeEnd = f.length() - 1;
        // 解析请求Range头信息,bytes=xxx-xxx, (存在0-0,-1这种情况,暂不考虑)
        if (rangeString.indexOf("-") < rangeString.length() - 1) {
            rangeEnd = Long.parseLong(rangeString.substring(rangeString.indexOf("-") + 1));
        }

        // chrome浏览器第一次发送请求,range开始为0
        if (rangeStart == 0) {
            // 没有这个header,audio没有总时长,第一次请求返回总长度
            response.setHeader("Content-Length", f.length() + "");
        } else {
            // 配合content-range头使用,后续Content-Length代表分段大小
            response.setStatus(HttpStatus.PARTIAL_CONTENT.value());
            response.setHeader("Content-Length", (rangeEnd - rangeStart + 1) + "");
        }

        // 不太重要
        response.setHeader("Content-Type", fileName.endsWith("wav") ? "audio/wav" : "audio/mp3");

        // range范围  start-end/total, 这个头可以不要,但是如果不要(content-length必须为文件总大小),每次拖拽,浏览器请求的文件都是全量大小的文件, 浪费带宽
        response.setHeader("Content-Range", "bytes " + rangeStart + "-" + rangeEnd + "/" + f.length());

        // 告知video / audio标签可以接受范围单位,这个头必须传递
        response.setHeader("Accept-Ranges", "bytes");

        // 不重要
        response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));

        // skip bytes
        if (rangeStart > 0) {
            long skipped = fis.skip(rangeStart);
            System.out.println(fileName + ", skipped: " + skipped + ", total: " + f.length());
        }

        byte[] buf = new byte[4096];
        int len;
        long total = 0L;
        while ((len = fis.read(buf)) != -1) {
            total += len;
            response.getOutputStream().write(buf, 0, len);
        }
        System.out.println(fileName + ", write: " + total + ", range: " + (rangeEnd - rangeStart));
        response.getOutputStream().flush();
    }

 

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐