概念确认
文件上传,在网页上通过Ajax异步或者同步Post Form将文件搞到服务器上。
文件服务,包含两个部分,文件上传客户端,文件接收服务端。
不把百度搜烂的同学不是好同学
百度 site:jfinal.com 文件上传 (都看看, 看看别人是什么被坑出翔, 有助于快速出坑.)
JFinal文件上传文档 (基础文档都不看, 谈别的都是耍流氓)
用httpUrlConnection实现文件上传 (HttpClient太繁琐, HttpKit挺好就是基于这个的, 所以得先看看理论)
用JFinal上传文件时报错:Separation boundary was not specified
第三方文件上传客户端 (还是各种报错, 同HttpClient, 太繁琐 OkHttp 比那玩意还好用一点点)
看到这个的同学, 可能绝望了, 波总把锅都甩给了客户端(^v^至少小白会这样认为, 但实际上真的是因为不会用客户端,以及不了解Http文件上传协议, 导致了这些令人窒息的报错, 道理我都懂, 就是上传报错,MMP)


看见没, 大佬在不同的时空解释了, 万恶的根源都是你不够了解http协议规范.
好了, 万恶的根源解释完了, 下面要亮绝招了!

作为一名Eovaer,一定要寻求最简单的套路,什么Http 协议并不想关心。
我相信和我一样学渣的你,一定是懒的,嫌麻烦的。
那么我也相信你写过增删改查,写过登录,写过API,写过post json,写过renderJson(Ret.ok());
如果这些都没搞过,连学渣都不如, 就可以考虑回炉重构了。
好至少写过post json, 懂这个套路就够了。
那下面再聊5毛钱的理论。
万物皆对象,万物皆字符串,万物皆1和0,那么由此可推导出 文件=File=String=byte[]
由此可得到一个理论 我会post json, 我就会 post file=我会上传文件.
OK, 顺着这个思路, 就可以完成我们今天要做的文件上传服务(客户端和服务端), 让操蛋的Http协议见鬼去吧, 我就不看.
理论结束, 下面上代码了.
com.jfinal.kit.HttpKit.class 核心代码 data.getBytes(CHARSET) 就是提交二进制文件内容
public static String post(String url, Map<String, String> queryParas, String data, Map<String, String> headers) {
HttpURLConnection conn = null;
try {
conn = getHttpConnection(buildUrlWithQueryString(url, queryParas), POST, headers);
conn.connect();
if (data != null) {
OutputStream out = conn.getOutputStream();
out.write(data.getBytes(CHARSET));
out.flush();
out.close();
}
return readResponseString(conn);
}
catch (Exception e) {
throw new RuntimeException(e);
}
finally {
if (conn != null) {
conn.disconnect();
}
}
}能提交 string txt json 为什么不能提交 jpg zip avi ?
你已经会提交 json, 提交zip, 不就差一个 zip to Bytes吗? 来来来....百度找了一个, 真香!
/**
* 文件转二进制
* @param filePath
* @return
* @throws IOException
*/
public static byte[] fileToByte(File file) {
byte[] bt = null;
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
bt = new byte[(int) file.length()];
fis.read(bt);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return bt;
}
有了File to 二进制, 反之呢?
/**
* 将inputStream转化为file
* @param is
* @param file 要输出的文件目录
*/
public static void inputStream2File(InputStream is, File file) throws IOException {
OutputStream os = null;
try {
os = new FileOutputStream(file);
int len = 0;
byte[] buffer = new byte[1024];
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
} finally {
os.close();
is.close();
}
}如上代码By eova-pro 3.4.1 com/eova/common/utils/io/FileUtil.java
public class TestController extends BaseController {
// 文件上传服务端
public void server() {
String fileName = get("fileName");
fileName = System.currentTimeMillis() + "@" + fileName;
System.out.println(fileName);
String baseDir = "C:\\server\\";
HttpServletRequest request = getRequest();
try {
File file = new File(baseDir + fileName);
FileUtil.inputStream2File(request.getInputStream(), file);
} catch (Exception e) {
renderJson(Ret.fail("msg", e.getMessage()));
return;
}
renderJson(Ret.ok("name", fileName));
}
// 文件上传客户端
public void client() {
String url = "http://127.0.0.1/test/server";
File file = new File("C:\\logs.rar");
String result = HttpUtils.cs().postFile(url, file.getName(), file);
renderText(result);
}
}PS: com.eova.common.utils.HttpUtils; 也是基于JFinal HttpKit改造的.
public String postFile(String url, String fileName, File file) {
this.contentType = "application/octet-stream";
// 你只post了一坨文件数据, 需要单独把文件名给弄过去.
HashMap<String, String> queryParas = new HashMap<>();
queryParas.put("fileName", fileName);
return post(url, queryParas, FileUtil.fileToByte(file), null);
}下面上测试图:
小结
这个方案主要用于日常小文件跨服务发送,比如私有云盘服务,传200M以内的文件是没啥问题的,如果是私有AV云还是考虑用115网盘吧,可以直接离线观看。

173M的EOVA视频教程, 传输时间是1.4S (内网), 基本上能满足大部分日常文件的需求了.

那我们挑战一下传一部AV 1.9GB

显然会内存溢出, 因为JVM默认没配这么多.转二进制就溢出了.
Caused by: java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOf(Arrays.java:3236) at java.io.ByteArrayOutputStream.grow(ByteArrayOutputStream.java:118) at java.io.ByteArrayOutputStream.ensureCapacity(ByteArrayOutputStream.java:93) at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:153) at sun.net.www.http.PosterOutputStream.write(PosterOutputStream.java:78) at java.io.OutputStream.write(OutputStream.java:75)
如何支持更大的文件-->增加JVM内存上限.
警告:如果文件较大,建议单独一个服务跑文件服务, 否则分分钟宕机.

基本上1G以内的文件是没啥问题的, 如果真要做AV云就要考虑更多的问题了, 分片上传, 多线程上传, 断点续传等问题.
JFinal-4.8 action report -------- 2020-03-29 13:33:57 -------------------------- Url : POST /test/server Controller : com.oss.test.TestController.(TestController.java:1) Method : server Interceptor : com.eova.interceptor.LoginInterceptor.(LoginInterceptor.java:1) com.eova.interceptor.AuthInterceptor.(AuthInterceptor.java:1) Parameter : fileName=111.mp4 -------------------------------------------------------------------------------- 上传成功: Load Cost Time:3220ms 文件大小: 375M
1585460867549@EKAI-003.avi 1.5G -Xms512m -Xmx4096m
Load Cost Time:21525ms 没有啥事是加内存解决不了的
总结:
无招胜有招
你永远不知道你有多优秀
你掌握的本领已经可以改变世界,可能还没融汇贯通
你在网上可能还能收到FTP上传,运维不给你权限你只能骂娘,这个方案是应用层解决文件上传与接收!
我们不生产源码,我们只生产各种奇技赢巧。
喜欢的可以去下载EOVA源码,扒各种骚操作。