-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
179 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
# 静态资源请求处理器ResourceHttpRequestHandler | ||
|
||
## 1.传统采用IO方式传输文件 | ||
用文件流方式,在springboot读取整个视频文件的文件流,写入HttpServletResponse的OutputStream。 | ||
```java | ||
private void preview(String filename, String path, HttpServletResponse response) { | ||
OutputStream os; | ||
try { | ||
response.addHeader("Content-disposition", "attachment;filename=" + | ||
URLEncoder.encode(filename, "UTF-8")); | ||
response.setCharacterEncoding("UTF-8"); | ||
byte[] bytes = FileUtil.readBytes(path + filename); | ||
os = response.getOutputStream(); | ||
os.write(bytes); | ||
os.flush(); | ||
os.close(); | ||
} catch (Exception e) { | ||
// System.out.println(e); | ||
} | ||
} | ||
|
||
``` | ||
但是这种方式很`消耗服务器内存`,而且前端那边得把整个视频下载好才能播放,而且如果视频很大那就会有很大的卡顿时间,对于用户体验是极差的。 | ||
|
||
虽然这种方法不适合传输视频等静态文件,但是如果是传输`动态生成`的文件,比如html和json文件,那就适合用这种方式。 | ||
|
||
|
||
## 2.采用静态资源请求处理器 | ||
使用http的rang来实现分片加载,也就是视频会有缓存,不会下载整个视频文件,而是下载视频的一部分进行播放,可以拖动进度条,快进等。 | ||
|
||
普遍的方法是根据前端要的文件片段,springboot加载本地文件,切分出具体那段文件,返回给前端去播放,但是实际上没必要那么麻烦。 | ||
|
||
因为`springboot自带`这方面的能力! | ||
|
||
ResourceHttpRequestHandler是springboot加载静态资源的一个类,平时是用来从resources/statics等目录加载文件的。所以,这个类本身就是支持range请求数据的。 | ||
主要用于`处理静态资源请求`,其内部实现可能会根据需要进行优化以提高性能。 | ||
|
||
### 实现代码 | ||
|
||
首先、创建一个bean NonStaticResourceHttpRequestHandler | ||
```java | ||
@Component | ||
public class NonStaticResourceHttpRequestHandler extends ResourceHttpRequestHandler { | ||
public final static String ATTR_FILE = "NON-STATIC-FILE"; | ||
@Override | ||
protected Resource getResource(HttpServletRequest request) { | ||
String filePath = (String) request.getAttribute(ATTR_FILE); | ||
return new FileSystemResource(filePath); | ||
} | ||
} | ||
``` | ||
其次、在控制层或者业务层使用这个bean | ||
```java | ||
@Autowired | ||
private NonStaticResourceHttpRequestHandler nonStaticResourceHttpRequestHandler; | ||
|
||
private void previewVideo(String filename, String path, HttpServletRequest request, HttpServletResponse response) { | ||
try { | ||
String filepath = path + filename; | ||
File file = new File(filepath); | ||
if (file.exists()) { | ||
request.setAttribute(NonStaticResourceHttpRequestHandler.ATTR_FILE, filepath); | ||
nonStaticResourceHttpRequestHandler.handleRequest(request, response); | ||
} else { | ||
response.setStatus(HttpServletResponse.SC_NOT_FOUND); | ||
response.setCharacterEncoding("UTF-8"); | ||
} | ||
} catch (Exception e) { | ||
response.setStatus(HttpServletResponse.SC_BAD_GATEWAY); | ||
System.out.println("播放视频错误!"); | ||
} | ||
} | ||
``` | ||
前端调用接口传入对应的视频文件名播放就可以了 | ||
|
||
|
||
|
||
## 思考 | ||
|
||
那么相比于传统的写入HttpServletResponse的OutputStream,ResourceHttpRequestHandler的区别在哪里? | ||
|
||
__首先,ResourceHttpRequestHandler通常用于处理像图片、CSS、JavaScript等`静态资源`的请求,这些资源通常会被缓存,并且请求处理逻辑相对简单。__ | ||
|
||
__相比之下,使用OutputStream直接写入HttpServletResponse通常用于`动态生成`内容,例如根据用户请求或数据库内容动态生成HTML页面或JSON数据。__ | ||
|
||
在静态资源处理方面,ResourceHttpRequestHandler可能具有优势,因为它可以针对静态资源的特性进行优化,例如使用高效的缓存机制、减少磁盘I/O等。然而,在处理动态内容生成时,使用OutputStream可能更加灵活和直接。 | ||
|
||
综上所述,ResourceHttpRequestHandler和OutputStream在处理不同类型请求时`各有优势`,其性能差异取决于具体的使用场景和配置。在实际应用中,应根据需求选择合适的处理方式。 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
# 2024年踩坑日志 | ||
|
||
## 服务器时间问题 | ||
后端需要调用第三方接口,第三方接口又有5秒钟时间差的限制,在本地环境不会有问题,但是部署到云服务器上就会请求出错。因为云服务器的系统时间和真正的本地时间是有差距的,这个差距不是一两秒,而是一两分钟,导致请求出错,但是云服务器的`硬件时间`又是准确的。 | ||
|
||
所以最后的解决方式是在云服务器里设定个`定时任务`,定时将系统时间与硬件时间`同步`。 | ||
|
||
## 公众号文章问题 | ||
原本客户想要在小程序上展示公众号文章,后面小程序链接公众号成功后,发现拿不到那么多文章。 | ||
|
||
后面了解到是微信小程序那边没有对应的接口,因为公众号的文章是分为`群发`与`发布`两个类型,如果文章是群发类型,就拿不到对应的公众号文章数据,因为微信没有提供群发类型文章接口,只提供了发布类型的文章接口,而因为群发类型会自动通知到关注公众号的用户,所以一般发布者都会设定成群发类型,导致拿不到数据。(纯微信的问题,一直没接口) | ||
|
||
最后只能取消这个需求,除非是客户愿意将群发文章都重新写一遍变成发布类型文章。 | ||
|
||
## 上传文件post请求报400 | ||
如果不是用ui库自带的upload组件里的action方式上传文件,而是采用自己post请求方式,会出现400错误。 | ||
|
||
明明在请求头已经"Content-Type": "multipart/form-data",但还是报错。 | ||
```javascript | ||
fetch('/api/upload', { | ||
method: 'POST', | ||
body: fd, | ||
headers: { | ||
'Content-Type': 'multipart/form-data' | ||
} | ||
}) | ||
``` | ||
而这个解决方法就是移除`"Content-Type": "multipart/form-data"`。 | ||
|
||
使用post 请求上传文件的时候是不需要自己设置 Content-Type,会自动给你添加一个 boundary ,用来分割消息主体中的每个字段,如果这个时候自己设置了 Content-Type, 服务器就不知道怎么分割各个字段,因此就会报错。 | ||
|
||
## 前端播放视频问题 | ||
前端video标签使用后端接口请求视频时,有些MP4视频会出现一直请求失败的情况,是因为默认浏览器是识别`H264编码格式`的MP4视频,但是如果后端给的是`H265编码格式`的MP4视频,前端就无法识别,导致一直请求,一直失败的情况。 | ||
|
||
要么就前端上传视频做个限制,使用mp4box.js查看上传文件的编码格式是否为H264;要么就后端进行处理,将上传的所有视频统一采用FFmpeg进行转码成H264的mp4视频,但是这样很吃后端服务器的性能,所以我这边采用的是前者的方式。 | ||
```javascript | ||
const checkMp4Mime = async (file) => { | ||
return await new Promise((resolve) => { | ||
if (file.type == "video/mp4") { | ||
const reader = new FileReader(); | ||
const buffer = reader.readAsArrayBuffer(file); | ||
reader.onload = async function (e) { | ||
let arrayBuffer = e.target.result; | ||
arrayBuffer.fileStart = 0; | ||
const mp4boxfile = MP4Box.createFile(); | ||
mp4boxfile.onReady = (info) => { | ||
const mime = info.mime; | ||
const codec = mime.split("; "); | ||
let res = false; | ||
codec.forEach((item) => { | ||
if (item.indexOf('codecs="') > -1 && item.indexOf('avc') > -1) res = true; | ||
}); | ||
if (res) { | ||
resolve(true); | ||
} else { | ||
resolve(false); | ||
window.$notification.warning({ | ||
message: "只能上传编码格式为H264的MP4视频", | ||
description: "浏览器播放mp4视频只支持H264编码格式", | ||
duration: 5, | ||
}); | ||
} | ||
}; | ||
mp4boxfile.appendBuffer(arrayBuffer); | ||
}; | ||
} else resolve(true); | ||
}); | ||
}; | ||
``` | ||
|
||
## 后端视频文件接口 | ||
如果是直接使用IO流去传输视频文件,那么前端得等整个视频文件下载完成才会去播放,当视频很大的时候,就需要等待很长的时间才能播放起来,而且对服务器的带宽压力也很大。 | ||
|
||
所以后端需要采用`静态资源请求处理器ResourceHttpRequestHandler`。 | ||
|
||
实现代码在`功能实现--静态资源请求处理器`中 |