基于JFinal开发私有云盘文件服务,如何不跪舔第三方库

概念确认

文件上传,在网页上通过Ajax异步或者同步Post Form将文件搞到服务器上。

文件服务,包含两个部分,文件上传客户端,文件接收服务端。

不把百度搜烂的同学不是好同学 

百度 site:jfinal.com 文件上传   (都看看, 看看别人是什么被坑出翔, 有助于快速出坑.)           

JFinal文件上传文档 (基础文档都不看, 谈别的都是耍流氓)

用httpUrlConnection实现文件上传 (HttpClient太繁琐, HttpKit挺好就是基于这个的, 所以得先看看理论)

用JFinal上传文件时报错:Separation boundary was not specified

第三方文件上传客户端 (还是各种报错, 同HttpClient, 太繁琐 OkHttp 比那玩意还好用一点点)
image.png

看到这个的同学, 可能绝望了, 波总把锅都甩给了客户端(^v^至少小白会这样认为, 但实际上真的是因为不会用客户端,以及不了解Http文件上传协议, 导致了这些令人窒息的报错, 道理我都懂, 就是上传报错,MMP)

image.png
image.png

看见没, 大佬在不同的时空解释了, 万恶的根源都是你不够了解http协议规范.

好了, 万恶的根源解释完了, 下面要亮绝招了!

image.png
作为一名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);
	}

下面上测试图:image.png

小结

这个方案主要用于日常小文件跨服务发送,比如私有云盘服务,传200M以内的文件是没啥问题的,如果是私有AV云还是考虑用115网盘吧,可以直接离线观看。

image.png

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

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

image.png

显然会内存溢出, 因为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内存上限. 

警告:如果文件较大,建议单独一个服务跑文件服务, 否则分分钟宕机.

image.png
基本上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源码,扒各种骚操作。

评论区

foam103

2020-03-29 11:01

问一下:1、最大支持多大的文件上传;2、断点接续问题如何处理。现在小文件还好处理,现在1G这种大文件头痛。

Jieven

2020-03-29 13:13

大文件上传属于另外一个层面的问题, 常规上传大文件都是痛点. 这个方案主要适用于跨服务的文件转移. 文件大小不能超过JVM内存, 否则会报
Caused by: java.lang.OutOfMemoryError: Java heap space
at com.eova.common.utils.io.FileUtil.fileToByte(FileUtil.java:274)

Jieven

2020-03-29 13:41

@foam103 你的心有多大文件就支持多大

杜福忠

2020-03-30 10:01

@Jieven 是我知道最骚的大神。。。私有云盘感觉还是用云存储便宜一点,比如阿里的“对象存储 OSS”还有华为的等,比自挂云磁盘便宜不少,存储型1T空间一月30元吧。。。技术归技术,应用在项目代码内置在线升级功能 或者插件在线更新等等功能,还是特别棒的

chcode

2020-03-30 13:11

@Jieven 奇技淫巧

jfinal009

2020-04-01 11:24

经典:万物皆对象,万物皆字符串,万物皆1和0,那么由此可推导出 文件=File=String=byte[]

JFinal

2020-04-01 16:15

直达事物本质,不留一点杂质,这是高手,点赞

Jieven

2020-04-02 22:23

@JFinal 班门弄一下斧

SM.Time

2020-04-03 08:53