前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >epan | 开发笔记

epan | 开发笔记

作者头像
yiyun
发布2022-04-01 16:58:22
发布2022-04-01 16:58:22
77500
代码可运行
举报
文章被收录于专栏:yiyun 的专栏yiyun 的专栏
运行总次数:0
代码可运行

引言

前端参考

文件传输

秒传

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检查)时,响应切片大小,前端使用此切片大小切片,这样只需要维护服务端对于切片大小的配置即可。

断点续传

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

参考:

代码语言:javascript
代码运行次数:0
运行
复制
"Content-Disposition","attachment;filename=FileName.txt"

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

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

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

参考:

application/x-www-form-urlencoded

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

代码语言:javascript
代码运行次数:0
运行
复制
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

代码语言:javascript
代码运行次数:0
运行
复制
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字符串 格式组织,放于请求体

代码语言:javascript
代码运行次数:0
运行
复制
POST http://www.example.com HTTP/1.1 
Content-Type: application/json;charset=utf-8

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

text/xml

代码语言:javascript
代码运行次数:0
运行
复制
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 失败

代码语言:javascript
代码运行次数:0
运行
复制
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)

解决:

代码语言:javascript
代码运行次数:0
运行
复制
Configuration conf = new Configuration();
conf.set("dfs.client.use.datanode.hostname", "true");

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

设置 Windows host 映射

代码语言:javascript
代码运行次数:0
运行
复制
# Docker - Hadoop
127.0.0.1 master

Docker 容器端口开放 50075

参考:

注意: 目测

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

成功 最终代码:

代码语言:javascript
代码运行次数:0
运行
复制
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 失败

代码语言:javascript
代码运行次数:0
运行
复制
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]

解决:

代码语言:javascript
代码运行次数:0
运行
复制
<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依赖

参考:

代码语言:javascript
代码运行次数:0
运行
复制
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及以下

代码语言:javascript
代码运行次数:0
运行
复制
<!-- 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

代码语言:javascript
代码运行次数:0
运行
复制
{
"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

参考:

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

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

Hadoop 常用端口号

参考:

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

MySQL 允许远程访问

参考:

进入 mysql

代码语言:javascript
代码运行次数:0
运行
复制
mysql -u root -p

操作mysql系统数据库

代码语言:javascript
代码运行次数:0
运行
复制
use mysql

查询用户表

代码语言:javascript
代码运行次数:0
运行
复制
select User,authentication_string,Host from user;

从上面,可以发现 rootlocalhost,即只允许本地访问,设置允许所有访问,即 % 授权 这里的123456为你给新增权限用户设置的密码,%代表所有主机,也可以具体到你的主机ip地址

代码语言:javascript
代码运行次数:0
运行
复制
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '123456';

刷新权限

代码语言:javascript
代码运行次数:0
运行
复制
flush privileges;

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

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

代码语言:javascript
代码运行次数:0
运行
复制
### 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. 致谢

参考

感谢帮助!

本文作者: yiyun

本文链接: https://cloud.tencent.com/developer/article/1970865

版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-06-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 引言
  • 前端参考
  • 文件传输
    • 秒传
      • JavaScript 计算 文件MD5
    • 分片上传
      • 方案1
      • 方案2
    • 断点续传
  • 响应文件 - 下载/浏览器查看
  • 四种常见的 POST 提交数据方式
    • application/x-www-form-urlencoded
    • multipart/form-data
    • application/json
    • text/xml
  • Q&A
  • 补充
    • 上传大文件到 HDFS 失败
    • mvn compile 失败
    • Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported
    • Date 没有具体时间 (m,s)
    • Hadoop 常用端口号
    • MySQL 允许远程访问
    • MyBatis: Table 'epan_moeci_com.userInfo' doesn't exist
    • 答辩总结
  • 参考
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档