Java Play Framework 2.3.x ByteChunks MP3流媒体没有播放功能,不是';可滚动';在浏览器中

Java Play Framework 2.3.x ByteChunks MP3流媒体没有播放功能,不是';可滚动';在浏览器中,java,audio,playframework,http-headers,streaming,Java,Audio,Playframework,Http Headers,Streaming,使用Play Framework(版本2.3.x)(Java风格),我试图向浏览器提供.mp3文件。由于它是一个“大”文件,我决定使用Play的ByteChunks对象,如下所示 @With(MP3Headers.class) public static Result test() { Chunks<byte[]> chunks = new ByteChunks() { public void onReady(Chunks.Out<byte[]>

使用Play Framework(版本
2.3.x
)(Java风格),我试图向浏览器提供
.mp3
文件。由于它是一个“大”文件,我决定使用Play的
ByteChunks
对象,如下所示

@With(MP3Headers.class)
public static Result test() {

    Chunks<byte[]> chunks = new ByteChunks() {
        public void onReady(Chunks.Out<byte[]> out) {
            try {
                byte[] song = Files.readAllBytes(Paths.get("public/mp3/song.mp3"));
                out.write(song);
            } catch(Exception e) {
                e.printStackTrace();
            } finally {
                out.close();
            }
        }
    };

    return ok(chunks);
}
为了完成,我的
路由
文件:

# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~

# Home page
GET     /                           controllers.Application.index()

GET     /test                       controllers.Application.test()

# Map static resources from the /public folder to the /assets URL path
GET     /assets/*file               controllers.Assets.at(path="/public", file) 
正如预期的那样,导航到
localhost:9000/test
将呈现一个漂亮的
HTML5
音频播放器(见图)

我的问题是音频播放器中的“滚动”不起作用。如果我滚动,音乐会暂停,当我放开时(当我在时间上“选择”一个位置时),音乐会在第一次暂停的地方继续


我希望我说的有道理,我希望你们对这件事了解得更多。提前感谢。

您需要告诉您的浏览器您的服务器支持范围请求并实现范围响应(即仅提供浏览器需要的音乐部分)。您可以在中获得请求/响应周期的概述

@With(MP3Headers.class)
公共静态结果测试(){
最终整数开始,结束;
最终布尔值isRangeReq;
response().setHeader(“接受范围”、“字节”);
if(request().hashreader(“RANGE”)){
isRangeReq=true;
字符串[]range=request().getHeader(“range”).split(“=”[1]。split(“-”);
begin=Integer.parseInt(范围[0]);
如果(range.length>1){
end=Integer.parseInt(范围[1]);
}否则{
end=song.length-1;
}
response().setHeader(“内容范围”,String.format(“字节%d-%d/%d”,开始,结束,歌曲.length”);
}否则{
isRangeReq=false;
开始=0;
结束=宋体长度-1;
}
Chunks Chunks=new ByteChunks(){
public void onReady(Chunks.Out){
如果(isRangeReq){
out.write(Arrays.copyOfRange(歌曲、开始、结束));
}否则{
写出(歌曲);
}
out.close();
}
};
response().setHeader(“内容长度”(end-begin+1)+”);
如果(isRangeReq){
返回状态(206,块);
}否则{
返回状态(200,块);
}
}

请注意,在此代码中,歌曲已加载到
song
。另外,
范围
头的解析非常脏(你可以得到像
范围:
这样的值)

我发现这段代码很容易实现。

将以下操作及其私有助手方法放入控制器中

控制器动作

流辅助方法


此处有完整的示例链接

谢谢!这很有帮助!好吧,我们最好通过解析
范围
标题的变通方法来改进代码。这个答案很有帮助,谢谢。现在的问题是,流在整个音频中都有光点和缺陷。你有没有想过是什么原因造成的,或者如何修复?
# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~

# Home page
GET     /                           controllers.Application.index()

GET     /test                       controllers.Application.test()

# Map static resources from the /public folder to the /assets URL path
GET     /assets/*file               controllers.Assets.at(path="/public", file) 
@With(MP3Headers.class)
public static Result test() {
    final int begin, end;
    final boolean isRangeReq;
    response().setHeader("Accept-Ranges", "bytes");
    if (request().hasHeader("RANGE")) {
        isRangeReq = true;
        String[] range = request().getHeader("RANGE").split("=")[1].split("-");
        begin = Integer.parseInt(range[0]);
        if (range.length > 1) {
            end = Integer.parseInt(range[1]);
        } else {
            end = song.length-1;
        }
        response().setHeader("Content-Range", String.format("bytes %d-%d/%d", begin, end, song.length));
    } else {
        isRangeReq = false;
        begin = 0;
        end = song.length - 1;
    }

    Chunks<byte[]> chunks = new ByteChunks() {
        public void onReady(Chunks.Out<byte[]> out) {
            if(isRangeReq) {
                out.write(Arrays.copyOfRange(song, begin, end));
            } else {
                out.write(song);
            }
            out.close();
        }
    };
    response().setHeader("Content-Length", (end - begin + 1) + "");
    if (isRangeReq) {
        return status(206, chunks);
    } else {
        return status(200, chunks);
    }
}
public static Result file(Long id, String filename) throws IOException {

Item item = Item.fetch(id);
File file = item.getFile();

if(file== null || !file.exists()) {
  Logger.error("File no longer exist item"+id+" filename:"+filename);           
  return notFound();
}

String rangeheader = request().getHeader(RANGE);
if(rangeheader != null) {

  String[] split = rangeheader.substring("bytes=".length()).split("-");
  if(Logger.isDebugEnabled()) { Logger.debug("Range header is:"+rangeheader); }

  if(split.length == 1) {
    long start = Long.parseLong(split[0]);
    long length = file.length()-1l;             
    return stream(start, length, file);                             
  } else {
    long start = Long.parseLong(split[0]);
    long length = Long.parseLong(split[1]);
    return stream(start, length, file);
  }
}

// if no streaming is required we simply return the file as a 200 OK
if(Play.isProd()) {
  response().setHeader("Cache-Control", "max-age=3600, must-revalidate");           
}
return ok(file);
}
private static Result stream(long start, long length, File file) throws IOException {

FileInputStream fis = new FileInputStream(file);
fis.skip(start);
response().setContentType(MimeTypes.forExtension("mp4").get());
response().setHeader(CONTENT_LENGTH, ((length - start) +1l)+"");
response().setHeader(CONTENT_RANGE, String.format("bytes %d-%d/%d", start, length,file.length()));

response().setHeader(ACCEPT_RANGES, "bytes");
response().setHeader(CONNECTION, "keep-alive");
return status(PARTIAL_CONTENT, fis);
}