epan | 开发笔记

引言

前端参考

文件传输

秒传

JavaScript 计算 文件MD5

参考:

分片上传

参考:

或者叫 分片传输,分片方案不一定用于浏览器上传服务端,也可以用于服务端到服务端,甚至服务端到客户端 (下载)

方案1

浏览器 -> 服务端

浏览器切割文件为n个切片,调用n次服务端上传接口,上传n个切片,

服务端接收切片,每次接收,内存中暂存每个切片,每接收到一个切片,则以 appendWrite 的方式追加到目标文件中(可以顺序appendWrite到文件末尾,保证接收切片的顺序正确,如果中间丢失某个切片,则响应浏览器切片序号,重传此切片;也可以计算偏移量,将切片插入目标文件中)

将文件切分为 n 个切片,除了最后一个切片外。其余每个切片固定 sliceSize 大小

切片序号: 0, 1, 2, 3,...

切片 在此文件中的偏移量 = 切片序号 * 单个切片大小

文件接收完时 (/合并时)

方案1:由前端发起http,告诉服务端合并切片

方案2: 第一次发送文件检查md5时,发送文件总大小,服务端记录下来,此后切片传输时,可通过 切片个数,文件总大小,切片大小,计算出什么时候文件接收完成,这时合并切片

缺:此种方法,需要一个临时路径用于存一个临时文件(上传一个文件时)

优:假设 appendWrite 时 为顺序插入切片,则无需数据库支持(保存切片序号信息),也可以记录上传进度,上传中断后,不清除临时文件,下次用户可以接着上传,结合临时文件的大小,与保存在数据库中的文件总大小,切片单个大小,计算出,当前位于第几个切片,需要从第几个切片处开始上传,则响应浏览器,上传对应切片

当浏览器上传切片时,服务端响应此切片上传成功后,浏览器可以记录此切片已上传成功,并更新上传进度条

方案2

服务单接收切片,每接收到一个切片,则将此切片存于 /md5file/part-2.tmp (单独的切片文件) 磁盘中,保存最后一个切片后,合并切片为一个文件,完成后清除切片文件,

缺:此种方法,需要一个临时路径用于存n个切片文件(上传一个文件时,需要n个切片文件)

优:1.由于是将切片保存到磁盘中,因此,可以断点续传,上传中断后,不清除切片文件,下次用户可以接着上传。

由于每个切片文件,文件名中包含了切片序号,因此无需数据库支持(保存切片序号),也不需要顺序支持

  1. 由于n个切片文件的存在,所以可以推断出,对于需要切片的文件来说,至少有两个切片,于是可以从第一个切片的大小,得知当时对文件切片的固定大小,当项目上线后,可能存在中途更换固定的切片大小,更换后,如果更换后,有用户继续上传之前的文件,由于切片大小的变更,只从记录的切片序号中,是无法计算每个切片在文件的起始终止偏移(前端也就无法切片),但可以从第一个切片大小中得出当时切片时的切片大小,于是可以变得可以计算。

一个理想的方案:应当是在 文件上传检查(md5检查)时,响应切片大小,前端使用此切片大小切片,这样只需要维护服务端对于切片大小的配置即可。

断点续传

响应文件 - 下载/浏览器查看

参考:

1
"Content-Disposition","attachment;filename=FileName.txt"

attachment 表示以附件方式下载,而不是直接用浏览器打开查看

当你在响应类型为 application/octet- stream 情况下使用了这个头信息的话,那就意味着你不想直接显示内容,而是弹出一个"文件下载"的对话框,接下来就是由你来决定"打开"还是"保存" 了

四种常见的 POST 提交数据方式

参考:

application/x-www-form-urlencoded

POST 提交数据的方式, 浏览器的原生 <form> 表单,如果不设置 enctype 属性,那么最终就会以 application/x-www-form-urlencoded 方式提交数据

1
2
3
4
POST http://www.example.com HTTP/1.1
Content-Type: application/x-www-form-urlencoded;charset=utf-8

title=test&sub%5B%5D=1&sub%5B%5D=2&sub%5B%5D=3

key 和 val 都进行了 URL 转码

JQuery 和 QWrap 的 Ajax,Content-Type 默认值都是「application/x-www-form-urlencoded;charset=utf-8」

multipart/form-data

POST 数据提交的方式,

使用表单上传文件时,必须让 <form> 表单的 enctype 等于 multipart/form-data

1
2
3
4
5
6
7
8
9
10
11
12
13
POST http://www.example.com HTTP/1.1
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwA

------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="text"

title
------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="file"; filename="chrome.png"
Content-Type: image/png

PNG ... content of chrome.png ...
------WebKitFormBoundaryrGKCBY7qhFd3TrwA--

application/json

内容以 json字符串 格式组织,放于请求体

1
2
3
4
POST http://www.example.com HTTP/1.1 
Content-Type: application/json;charset=utf-8

{"title":"test","sub":[1,2,3]}

text/xml

1
2
3
4
5
6
7
8
9
10
11
12
POST http://www.example.com HTTP/1.1 
Content-Type: text/xml

<?xml version="1.0"?>
<methodCall>
<methodName>examples.getStateName</methodName>
<params>
<param>
<value><i4>41</i4></value>
</param>
</params>
</methodCall>

Q&A

Q: 服务端如何提供大文件下载?

A:

Q: 服务端提供视频播放直链,难道直接一次性将文件内容返回,这样不是很占用服务端内存吗?是否改切片传输给浏览器?

A:

Q: 针对大文件上传使用 切片上传,如果对接其它云存储(eg, 阿里云OSS),

方案1:直接服务端(内存中)接收切片,直接传输到OSS,最后在 OSS 中合并切片

方案2:n个切片暂存在服务端磁盘中,合并后,再(边读边写)传输给OSS

方案3:直接服务端(内存中)接收切片,直接传输到OSS,以追加到文件末尾方式 写入OSS 单个文件,写完即合并完

各种方案利弊?

A:

Q: 边读边写?追加到文件末尾?

A: 其实 while (追加到文件末尾) 的方式,就是 边读边写,

不过边读边写,没有强调插入位置(不一定要插入末尾),而 追加到文件末尾强调了插入位置在最后

补充

上传大文件到 HDFS 失败

1
2
org.apache.hadoop.ipc.RemoteException: File /epan-hdfs/2021/06/20/e4133d04-5c6f-465c-911b-7b9da25c8e56 could only be replicated to 0 nodes instead of minReplication (=1).  There are 1 datanode(s) running and 1 node(s) are excluded in this operation.
at org.apache.hadoop.hdfs.server.blockmanagement.BlockManager.chooseTarget4NewBlock(BlockManager.java:1571)

解决:

1
2
3
4
Configuration conf = new Configuration();
conf.set("dfs.client.use.datanode.hostname", "true");

fs = FileSystem.get(URI.create(hdfsURI), conf);

设置 Windows host 映射

1
2
# Docker - Hadoop
127.0.0.1 master

Docker 容器端口开放 50075

参考:

  • https://blog.csdn.net/fragrant_no1/article/details/85844657
  • https://my.oschina.net/niuruijing/blog/3002550
  • https://blog.csdn.net/chenyuangege/article/details/53217713

注意:

目测

目测主要是50010 端口 用于 宿主机访问 DataNode

成功 最终代码:

1
2
3
4
5
Configuration conf = new Configuration();
conf.set("dfs.client.use.datanode.hostname", "true");
conf.set("dfs.replication", "1");

fs = FileSystem.get(URI.create(hdfsURI), conf);

mvn compile 失败

1
Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources) on project epan: Input length = 1 -> [Help 1]

解决:

pom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

plugins标签添加 maven-resources-plugin依赖

参考:

1
The POM for com.alibaba:druid:jar:1.1.21 is invalid, transitive dependencies (if any) will not be available, enable debug logging for more details

解决:

driud的依赖从1.2.21降到1.2.20及以下

pom.xml
1
2
3
4
5
6
<!-- Druid 数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.20</version>
</dependency>

参考:

Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported

1
2
3
4
5
6
7
8
{
"timestamp": "2021-06-21T02:55:50.737+00:00",
"status": 415,
"error": "Unsupported Media Type",
"trace": "org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported\r\n\tat org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:206)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:158)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:131)\r\n\tat org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:170)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:894)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)\r\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1063)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)\r\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)\r\n\tat org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)\r\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:652)\r\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)\r\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:733)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\r\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\r\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\r\n\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\r\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\r\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)\r\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)\r\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)\r\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143)\r\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)\r\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)\r\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357)\r\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374)\r\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)\r\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:893)\r\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1707)\r\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\r\n\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)\r\n\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)\r\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\r\n\tat java.base/java.lang.Thread.run(Thread.java:832)\r\n",
"message": "Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported",
"path": "//api/file/uploadCheck"
}

不是:@RequestBody 改为 @RequestParam,而是

当需要从请求体中取 json格式数据时,使用 @RequestBody ,并且将前端请求时的Content-Type 改为 application/json

参考:

  • https://www.cnblogs.com/yuhuameng/p/10706674.html

  • https://blog.csdn.net/feiyst/article/details/88431621

Date 没有具体时间 (m,s)

Java: Date 类有具体时间,但 MySQL 中 date 类型只有日期,需要更改为 datetime

Hadoop 常用端口号

参考:

通信hadoop平台需要开通的端口

MySQL 允许远程访问

参考:

进入 mysql

1
mysql -u root -p

操作mysql系统数据库

1
use mysql

查询用户表

1
select User,authentication_string,Host from user;

从上面,可以发现 rootlocalhost,即只允许本地访问,设置允许所有访问,即 %

授权

这里的123456为你给新增权限用户设置的密码,%代表所有主机,也可以具体到你的主机ip地址

1
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '123456';

刷新权限

1
flush privileges;

再次查询,发现多了一个用户,此时,允许所有远程访问

MyBatis: Table 'epan_moeci_com.userInfo' doesn't exist

1
2
3
4
5
6
7
### Error querying database.  Cause: java.sql.SQLSyntaxErrorException: Table 'epan_moeci_com.userInfo' doesn't exist
### The error may exist in file [F:\Com\me\Repos\epan\target\classes\mapper\UserMapper.xml]
### The error may involve defaultParameterMap
### The error occurred while setting parameters
### SQL: select id, userName, password, diskSize, usedDiskSize, ipAddress, createTime from userInfo where userName = ?
### Cause: java.sql.SQLSyntaxErrorException: Table 'epan_moeci_com.userInfo' doesn't exist
; bad SQL grammar []; nested exception is java.sql.SQLSyntaxErrorException: Table 'epan_moeci_com.userInfo' doesn't exist] with root cause

解决:

原因1

因为数据库对表的大小写设置问题,设置忽略大小写即可

在服务运行目录找到my.ini或者my.cnf文件 打开文件,找到[mysqld]在下面增加一行

lower_case_table_names=1 (0:大小写敏感;1:大小写不敏感)

重启MySQL服务

原因2

可能是多个数据库存在相同表,

因此在导入 install.sql 时,尤其注意,SQLyog 在导出 sql 时,

CREATE DATABASE语句,会创建新数据库,而不是导入目标数据库

答辩总结

PPT制作

  1. 背景
  2. 项目介绍
  3. 功能及运行展示
  4. 难点介绍及解决
  5. 成员介绍
  6. 致谢

参考

感谢帮助!