gpt4 book ai didi

java - 为 Spring Boot 实现字节服务

转载 作者:行者123 更新时间:2023-12-03 20:02:18 25 4
gpt4 key购买 nike

我想使用 Spring Boot Rest API 在 Angular 中实现视频播放器。我可以播放视频,但我无法进行视频搜索。每次当我使用 Chrome 或 Edge 时,视频都会一遍又一遍地播放。
我试过这个端点:

@RequestMapping(value = "/play_video/{video_id}", method = RequestMethod.GET)
@ResponseBody public ResponseEntity<byte[]> getPreview1(@PathVariable("video_id") String video_id, HttpServletResponse response) {
ResponseEntity<byte[]> result = null;
try {
String file = "/opt/videos/" + video_id + ".mp4";
Path path = Paths.get(file);
byte[] image = Files.readAllBytes(path);

response.setStatus(HttpStatus.OK.value());
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentLength(image.length);
result = new ResponseEntity<byte[]>(image, headers, HttpStatus.OK);
} catch (java.nio.file.NoSuchFileException e) {
response.setStatus(HttpStatus.NOT_FOUND.value());
} catch (Exception e) {
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
}
return result;
}
我发现这篇文章给出了一些想法: How to Implement HTTP byte-range requests in Spring MVC
但目前它不起作用。当我尝试移动位置时,视频又从头开始播放。
我用这个播放器: https://github.com/smnbbrv/ngx-plyr
我是这样配置的:
<div class="media">
<div
class="class-video mr-3 mb-1"
plyr
[plyrPlaysInline]="true"
[plyrSources]="gymClass.video"
(plyrInit)="player = $event"
(plyrPlay)="played($event)">
</div>
<div class="media-body">
{{ gymClass.description }}
</div>
</div>
你知道我该如何解决这个问题吗?

最佳答案

第一个解决方案:使用 FileSystemResourceFileSystemResource 内部处理字节范围 header 支持,读取和写入适当的 header 。
这种方法有两个问题。

  • 它使用 FileInputStream内部用于读取文件。这适用于小文件,但不适用于通过字节范围请求提供服务的大文件。 FileInputStream将从头开始读取文件并丢弃不需要的内容,直到它重新获得请求的起始偏移量。这可能会导致较大文件的速度变慢。
  • 它集 "application/json""Content-Type"响应头。所以,我提供我自己的 "Content-Type"标题。 See this thread
  • import java.nio.file.Path;
    import java.nio.file.Paths;
    import org.springframework.core.io.FileSystemResource;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.ResponseBody;

    @Controller
    public class Stream3 {
    @GetMapping(value = "/play_video/{video_id}")
    @ResponseBody
    public ResponseEntity<FileSystemResource> stream(@PathVariable("video_id") String video_id) {
    String filePathString = "/opt/videos/" + video_id + ".mp4";
    final HttpHeaders responseHeaders = new HttpHeaders();
    responseHeaders.add("Content-Type", "video/mp4");
    return new ResponseEntity<>(new FileSystemResource(filePathString), responseHeaders, HttpStatus.OK);
    }
    }
    第二种解决方案:使用 HttpServletResponseRandomAccessFileRandomAccessFile您可以实现对字节范围请求的支持。比 FileInputStream的优势, 是您不需要在每次有新范围请求时从头开始读取文件,这使得该方法也可用于较大的文件。 RandomAccessFile有一个方法叫 seek(long)调用 C 方法 fseek() ,它直接将文件的指针移动到请求的偏移量。
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.io.RandomAccessFile;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import javax.servlet.http.HttpServletResponse;
    import org.springframework.http.HttpStatus;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestHeader;
    import org.springframework.web.bind.annotation.ResponseBody;

    @Controller
    public class Stream {
    @GetMapping(value = "/play_video/{video_id}")
    @ResponseBody
    public void stream(
    @PathVariable("video_id") String video_id,
    @RequestHeader(value = "Range", required = false) String rangeHeader,
    HttpServletResponse response) {

    try {
    OutputStream os = response.getOutputStream();
    long rangeStart = 0;
    long rangeEnd;
    String filePathString = "/opt/videos/" + video_id + ".mp4";
    Path filePath = Paths.get(filePathString);
    Long fileSize = Files.size(filePath);
    byte[] buffer = new byte[1024];
    RandomAccessFile file = new RandomAccessFile(filePathString, "r");
    try (file) {
    if (rangeHeader == null) {
    response.setHeader("Content-Type", "video/mp4");
    response.setHeader("Content-Length", fileSize.toString());
    response.setStatus(HttpStatus.OK.value());
    long pos = rangeStart;
    file.seek(pos);
    while (pos < fileSize - 1) {
    file.read(buffer);
    os.write(buffer);
    pos += buffer.length;
    }
    os.flush();
    return;
    }

    String[] ranges = rangeHeader.split("-");
    rangeStart = Long.parseLong(ranges[0].substring(6));
    if (ranges.length > 1) {
    rangeEnd = Long.parseLong(ranges[1]);
    } else {
    rangeEnd = fileSize - 1;
    }
    if (fileSize < rangeEnd) {
    rangeEnd = fileSize - 1;
    }

    String contentLength = String.valueOf((rangeEnd - rangeStart) + 1);
    response.setHeader("Content-Type", "video/mp4");
    response.setHeader("Content-Length", contentLength);
    response.setHeader("Accept-Ranges", "bytes");
    response.setHeader("Content-Range", "bytes" + " " + rangeStart + "-" + rangeEnd + "/" + fileSize);
    response.setStatus(HttpStatus.PARTIAL_CONTENT.value());
    long pos = rangeStart;
    file.seek(pos);
    while (pos < rangeEnd) {
    file.read(buffer);
    os.write(buffer);
    pos += buffer.length;
    }
    os.flush();

    }

    } catch (FileNotFoundException e) {
    response.setStatus(HttpStatus.NOT_FOUND.value());
    } catch (IOException e) {
    response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
    }

    }

    }

    第三种解决方案:也使用 RandomAccessFile ,但是 StreamingResponseBody而不是 HttpServletResponse
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.RandomAccessFile;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestHeader;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;

    @Controller
    public class Stream2 {
    @GetMapping(value = "/play_video/{video_id}")
    @ResponseBody
    public ResponseEntity<StreamingResponseBody> stream(
    @PathVariable("video_id") String video_id,
    @RequestHeader(value = "Range", required = false) String rangeHeader) {
    try {
    StreamingResponseBody responseStream;
    String filePathString = "/opt/videos/" + video_id + ".mp4";
    Path filePath = Paths.get(filePathString);
    Long fileSize = Files.size(filePath);
    byte[] buffer = new byte[1024];
    final HttpHeaders responseHeaders = new HttpHeaders();

    if (rangeHeader == null) {
    responseHeaders.add("Content-Type", "video/mp4");
    responseHeaders.add("Content-Length", fileSize.toString());
    responseStream = os -> {
    RandomAccessFile file = new RandomAccessFile(filePathString, "r");
    try (file) {
    long pos = 0;
    file.seek(pos);
    while (pos < fileSize - 1) {
    file.read(buffer);
    os.write(buffer);
    pos += buffer.length;
    }
    os.flush();
    } catch (Exception e) {}
    };
    return new ResponseEntity<>(responseStream, responseHeaders, HttpStatus.OK);
    }

    String[] ranges = rangeHeader.split("-");
    Long rangeStart = Long.parseLong(ranges[0].substring(6));
    Long rangeEnd;
    if (ranges.length > 1) {
    rangeEnd = Long.parseLong(ranges[1]);
    } else {
    rangeEnd = fileSize - 1;
    }
    if (fileSize < rangeEnd) {
    rangeEnd = fileSize - 1;
    }

    String contentLength = String.valueOf((rangeEnd - rangeStart) + 1);
    responseHeaders.add("Content-Type", "video/mp4");
    responseHeaders.add("Content-Length", contentLength);
    responseHeaders.add("Accept-Ranges", "bytes");
    responseHeaders.add("Content-Range", "bytes" + " " + rangeStart + "-" + rangeEnd + "/" + fileSize);
    final Long _rangeEnd = rangeEnd;
    responseStream = os -> {
    RandomAccessFile file = new RandomAccessFile(filePathString, "r");
    try (file) {
    long pos = rangeStart;
    file.seek(pos);
    while (pos < _rangeEnd) {
    file.read(buffer);
    os.write(buffer);
    pos += buffer.length;
    }
    os.flush();
    } catch (Exception e) {}
    };
    return new ResponseEntity<>(responseStream, responseHeaders, HttpStatus.PARTIAL_CONTENT);

    } catch (FileNotFoundException e) {
    return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    } catch (IOException e) {
    return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
    }
    }
    }

    在你的 component.ts 中:
    您可以使用 playVideoFile() 更改当前显示的视频
    export class AppComponent implements OnInit {
    videoSources: Plyr.Source[];
    ngOnInit(): void {
    const fileName = 'sample';
    this.playVideoFile(fileName);
    }

    playVideoFile(fileName: string) {
    this.videoSources = [
    {
    src: `http://localhost:8080/play_video/${fileName}`,
    },
    ];
    }
    }

    和 html:
    <div
    #plyr
    plyr
    [plyrPlaysInline]="false"
    [plyrSources]="videoSources"
    ></div>

    关于java - 为 Spring Boot 实现字节服务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65492071/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com