Skip to content

Commit

Permalink
add mistake + function
Browse files Browse the repository at this point in the history
  • Loading branch information
lily0325 committed Sep 25, 2024
1 parent 467825c commit 63b29ee
Show file tree
Hide file tree
Showing 7 changed files with 179 additions and 17 deletions.
23 changes: 7 additions & 16 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,37 +28,28 @@ export default defineConfig({
link: '/introduction'
},
{
text: '前端',
text: '功能实现',
collapsed: false,
items: [
{ text: '快速开始', link: '/gettingStarted' },
{ text: '功能使用', link: '/function' },
{ text: '静态资源请求处理器', link: '/function/ResourceHttpRequestHandler' },
// { text: '功能使用', link: '/function' },
],
},
{
text: '后端',
text: '踩坑记录',
collapsed: false,
items: [
{ text: '快速开始', link: '/gettingStarted' },
{ text: '功能使用', link: '/function' },
{ text: '2024踩坑日志', link: '/mistake/2024' },
],
},
{
text: '知识',
collapsed: false,
items: [
{ text: 'WebSocket', link: '/doc/ws' },
{ text: 'SSE', link: '/doc/sse' },
{ text: 'WebSocket', link: '/knowledge/ws' },
{ text: 'SSE', link: '/knowledge/sse' },
],
},
{
text: '功能',
collapsed: false,
items: [
{ text: '快速开始', link: '/gettingStarted' },
{ text: '功能使用', link: '/function' },
],
}
],
},
});
88 changes: 88 additions & 0 deletions docs/function/ResourceHttpRequestHandler.md
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在处理不同类型请求时`各有优势`,其性能差异取决于具体的使用场景和配置。在实际应用中,应根据需求选择合适的处理方式。
7 changes: 7 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,11 @@ hero:
link: https://github.com/lily0325/lily0325.github.io
image:
src: https://img.icons8.com/?size=500&id=46393&format=png&color=000000
features:
- icon: 📝
title: 知识组织与检索
details: 通过建立资料库,你可以系统地组织和分类你的学习资源,如教程、代码片段、笔记和参考资料。这不仅有助于你更好地理解和消化所学知识,还能在将来需要时快速定位和检索相关信息,提高工作效率。
- icon: 🚀
title: 持续学习与自我提升
details: 一个结构化的资料库鼓励持续的学习和积累。每次学习新技能或遇到难题时,可以将其记录下来,形成自己的知识体系。随着时间的推移,这个资料库将成为个人成长和技能提升的重要见证,同时也便于回顾和复习,确保知识的长期记忆和应用。
---
2 changes: 1 addition & 1 deletion docs/introduction.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 个人开发学习资料库
# 个人开发学习资料库📝

## 概述
这是一个专门为我(或你)设立的开发学习资料资源库,旨在集中管理所有与个人技能提升、项目开发相关的文档和资源。它是我个人知识体系的一部分,帮助我在职业生涯中不断进步。
Expand Down
File renamed without changes.
File renamed without changes.
76 changes: 76 additions & 0 deletions docs/mistake/2024.md
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`

实现代码在`功能实现--静态资源请求处理器`

0 comments on commit 63b29ee

Please sign in to comment.