引言
作为开发人员,我们经常需要存储和下载文件,为了使用方便,通常都会将文件存储在云端,市面上使用率最高的云端存储莫过于百度网盘了,但使用别人的东西难免会受到各种各样的限制,必须花钱才会享受到更好的服务体验,所以很多公司和个人都会搭建自己的云盘系统来使用,一来文件捏在自己手里,更放心,更安全,二来可以根据自己的需求定制一些个性化服务。
最近公司也要做一个自己的存储服务,要求支持大文件断点续传,在线解压缩等功能,由于是内部项目,自由度相对较高,所以我在网上找了一个开源项目 - 奇文网盘,在它的基础上,修复了上传时候进度提示显示有误的 bug,同时添加了对 rar5 文件的在线解压支持,剔除了一些繁琐的功能,同时利用 JxBrowser 套壳实现了一个客户端版本。在此分享给大家,希望能够对你有所帮助,具体的源码资料可在文末获取哦。
系统展示
首页
系统首页主要对系统功能进行了描述和展示。
注册
注册页面提供滑动解锁插件,在输入用户名、手机号、密码之后,点击注册就可以注册成功。
登录
输入手机号和密码,滑动解锁,点击登录按钮即可登录。
网盘主页
网盘主页采用左右布局,并且做了屏幕自适应,包含了网盘的主要功能模块。
大文件断点续传剖析
系统前端的核心代码主要位于 src\components\file\box\uploadFile\Box.vue 文件中,里面包含了大文件的断点续传等操作,我们借助了 vue-simple-upload 组件辅助实现大文件的断点续传。
系统后端的核心代码主要位于 controller 包下的 FileController 和 FiletransferController 两个类,里面包含了对文件的上传,下载以及各种操作的 api 实现。
难点主要在于超大文件的断点续传,比如刷新页面,停电等导致上传中断,再次上传该文件时可以从之前中断的位置继续上传,而不必从头开始,断点续传主要分为以下几个步骤:
- 前端上传文件时,获取文件 md5 值,作为文件的唯一标识,获取文件大小,根据每个切片的大小(自定义,前后台保持一致)计算总切片数目,发送 md5 和切片信息请求后台;
- 后端拿到 md5 值以后,查询到该文件,根据切片信息判断文件是否已经上传完毕,如果已经上传完毕,则不用重新再上传,达到极速秒传的效果,若没有上传完毕,把已经上传的切片数组返回给前台,如 [1,2,3],没有则返回空;
- 前端对大文件进行切片。比如一个 100M 的文件,一个分片是 5M 的话,那么这个文件可以分20次上传。
- 前端请求后台发送切片,跳过已经上传的切片,每个切片上传的时候需要发送切片的具体信息:如切片大小,所有切片数,总大小,当前切片大小,当前切片数以及 md5 码等
- 后台接收切片上传请求,保存切片的相关信息,对上传的切片进行判断,当最后一个切片上传完毕后,合并所有切片,上传完毕。
具体流程图如下:
以上便是对大文件断点续传的简单剖析,理解原理之后,代码实现就不再是难事,感兴趣的朋友可以在文末获取相关源码资料自行研究,这里我们不过多赘述。
后端部署
系统数据库表结构无需手动初始化,启动的时候 jpa 会自动检查,生成表结构。
数据需要手动初始化一下,复制以下内容执行即可:
delete from user where userId = 1;
insert into user (userId, username, telephone, salt, password, available) values (1, 'admin', 'admin', 'admin', 'df655ad8d3229f3269fad2a8bab59b6c', 1);
delete from role where roleId in (1, 2);
INSERT INTO `role` (`roleId`, `available`, `description`, `roleName`, `createTime`, `createUserId`, `modifyTime`, `modifyUserId`) VALUES (1, 1, '超级管理员', '超级管理员', NULL, NULL, '2021-11-10 20:46:06', NULL);
INSERT INTO `role` (`roleId`, `available`, `description`, `roleName`, `createTime`, `createUserId`, `modifyTime`, `modifyUserId`) VALUES (2, 1, '普通用户', '普通用户', NULL, NULL, NULL, NULL);
delete from sysparam where sysParamId in (1, 2, 3);
insert into sysparam (sysParamId, sysParamKey, sysParamValue, sysParamDesc) values (1, 'totalStorageSize', '10240', '总存储大小(单位M)');
insert into sysparam (sysParamId, sysParamKey, sysParamValue, sysParamDesc) values (2, 'initDataFlag', '1', '系统初始化数据标识');
insert into sysparam (sysParamId, sysParamKey, sysParamValue, sysParamDesc) values (3, 'version', '1.1.2', '当前脚本的版本号');
delete from filetype where fileTypeId in (0, 1, 2, 3, 4, 5);
INSERT INTO `filetype` (`fileTypeId`, `fileTypeName`) VALUES (0, '全部');
INSERT INTO `filetype` (`fileTypeId`, `fileTypeName`) VALUES (1, '图片');
INSERT INTO `filetype` (`fileTypeId`, `fileTypeName`) VALUES (2, '文档');
INSERT INTO `filetype` (`fileTypeId`, `fileTypeName`) VALUES (3, '视频');
INSERT INTO `filetype` (`fileTypeId`, `fileTypeName`) VALUES (4, '音乐');
INSERT INTO `filetype` (`fileTypeId`, `fileTypeName`) VALUES (5, '其他');
delete from fileextend where 1 = 1;
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('bmp');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('jpg');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('png');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('tif');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('gif');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('jpeg');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('doc');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('docx');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('docm');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('dot');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('dotx');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('dotm');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('odt');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('fodt');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('ott');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('rtf');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('txt');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('html');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('htm');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('mht');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('xml');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('pdf');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('djvu');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('fb2');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('epub');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('xps');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('xls');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('xlsx');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('xlsm');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('xlt');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('xltx');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('xltm');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('ods');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('fods');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('ots');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('csv');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('pps');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('ppsx');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('ppsm');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('ppt');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('pptx');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('pptm');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('pot');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('potx');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('potm');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('odp');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('fodp');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('otp');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('hlp');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('wps');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('avi');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('mp4');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('mpg');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('mov');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('swf');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('wav');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('aif');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('au');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('mp3');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('ram');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('wma');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('mmf');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('amr');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('aac');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('flac');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('java');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('js');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('css');
INSERT INTO `fileextend` (`fileExtendName`) VALUES ('json');
delete from fileclassification where 1 = 1;
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (1, 1, 'bmp');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (2, 1, 'jpg');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (3, 1, 'png');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (4, 1, 'tif');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (5, 1, 'gif');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (6, 1, 'jpeg');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (7, 2, 'doc');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (8, 2, 'docx');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (9, 2, 'docm');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (10, 2, 'dot');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (11, 2, 'dotx');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (12, 2, 'dotm');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (13, 2, 'odt');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (14, 2, 'fodt');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (15, 2, 'ott');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (16, 2, 'rtf');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (17, 2, 'txt');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (18, 2, 'html');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (19, 2, 'htm');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (20, 2, 'mht');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (21, 2, 'xml');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (22, 2, 'pdf');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (23, 2, 'djvu');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (24, 2, 'fb2');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (25, 2, 'epub');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (26, 2, 'xps');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (27, 2, 'xls');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (28, 2, 'xlsx');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (29, 2, 'xlsm');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (30, 2, 'xlt');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (31, 2, 'xltx');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (32, 2, 'xltm');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (33, 2, 'ods');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (34, 2, 'fods');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (35, 2, 'ots');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (36, 2, 'csv');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (37, 2, 'pps');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (38, 2, 'ppsx');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (39, 2, 'ppsm');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (40, 2, 'ppt');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (41, 2, 'pptx');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (42, 2, 'pptm');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (43, 2, 'pot');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (44, 2, 'potx');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (45, 2, 'potm');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (46, 2, 'odp');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (47, 2, 'fodp');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (48, 2, 'otp');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (49, 2, 'hlp');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (50, 2, 'wps');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (51, 2, 'java');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (52, 2, 'js');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (53, 2, 'css');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (54, 2, 'json');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (55, 3, 'avi');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (56, 3, 'mp4');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (57, 3, 'mpg');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (58, 3, 'mov');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (59, 3, 'swf');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (60, 4, 'wav');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (61, 4, 'aif');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (62, 4, 'au');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (63, 4, 'mp3');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (64, 4, 'ram');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (65, 4, 'wma');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (66, 4, 'mmf');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (67, 4, 'amr');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (68, 4, 'aac');
INSERT INTO `fileclassification` (`fileClassificationId`, `fileTypeId`, `fileExtendName`) VALUES (69, 4, 'flac');
系统开发环境 dev 使用 h2 数据库,方便开发调试,项目启动成功后访问 http://localhost:8080/h2-concole 便可以对数据库进行管理维护;正式环境 prod 使用 mysql 数据库,通过修改 application.properties 中的 spring.profiles.active 实现环境切换。
部署也很简单,把项目打成 jar 包丢到服务器执行 java -jar xxx.jar 即可。
前端部署
前端执行 npm run serve 可以在开发环境调试运行,生产环境需要执行 npm run build 打包,打包后根目录下会生成文件夹 dist,我们这里选择使用 nginx 进行反向代理,将 dist 文件夹下的文件放置于 nginx/html 目录下,并配置 nginx/conf/nginx.conf,具体配置如下:
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
tcp_nopush on;
tcp_nodelay on;
types_hash_max_size 2048;
client_max_body_size 2048m;
client_header_buffer_size 512k;
large_client_header_buffers 4 512k;
server {
listen 80;
server_name localhost;
location /{
root html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
location /api/{
#proxy_set_hearder host $host;
#proxy_set_header X-forwarded-for $proxy_add_x_forwarded_for;
#proxy_set_header X-real-ip $remote_addr;
# 配置此处用于获取客户端的真实IP
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://localhost:8080/;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
nginx 配置完毕重启,访问 http://localhost 便可以成功访问:
套壳实现客户端
作为一个网盘应用,自然不能少了客户端,相信在平时的开发中我们也不时会遇到客户要求使用客户端的场景,我的客户在之前的项目中就提过此类的需求,但我相信做 java 开发的大部分都是 web 开发,对于桌面应用的开发基本都不擅长,那么问题来了,怎么用最小的成本把当前的 web 应用转化为桌面应用并且可以保证平台兼容性呢,当时我的客户就要求能在 windows,mac,linux 和国产麒麟系统上运行,可以说是很变态了,我当时寻求解决方案也花了好一阵时间。
我试过前台的 electron,JavaFx 等,实现效果都不理想,最后终于发现了一款兼容性极好,实现效果良好的客户端套壳神器:JxBrower,JxBrower 的功能十分强大,他是使用 Java 开发,内置了 chrome 的内核,并且可以对 js 和 Java 程序进行桥接,实现两者之间的交互,同时也可以监听浏览器中常见的事件,定制自己的个性化业务需求,唯一美中不足的,它是付费插件,想要试用的朋友可以去官网申请试用 license,可以免费使用 30 天,也可以在公众号直接联系我哦。
此项目我正是利用 JxBrower 实现了网盘客户端,代码如下:
package com.btzh;
import com.teamdev.jxbrowser.browser.Browser;
import com.teamdev.jxbrowser.browser.callback.StartDownloadCallback;
import com.teamdev.jxbrowser.download.Download;
import com.teamdev.jxbrowser.download.DownloadTarget;
import com.teamdev.jxbrowser.download.event.DownloadUpdated;
import com.teamdev.jxbrowser.engine.Engine;
import com.teamdev.jxbrowser.engine.EngineOptions;
import com.teamdev.jxbrowser.view.swing.BrowserView;
import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.nio.file.Paths;
import static com.teamdev.jxbrowser.engine.RenderingMode.HARDWARE_ACCELERATED;
/**
* @author zrx
*/
public class Main {
public static void main(String[] args) {
Engine engine = Engine.newInstance(EngineOptions.newBuilder(HARDWARE_ACCELERATED)
.licenseKey("自己申请的试用license")
.build());
// Create a Browser instance.
Browser browser = engine.newBrowser();
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("螺旋云盘");
frame.setIconImage(Toolkit.getDefaultToolkit().getImage(Main.class.getResource(
"/com/btzh/pan.png")));
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
// Shutdown Chromium and release allocated resources.
System.exit(0);
//engine.close();
}
});
// Create and embed Swing BrowserView component to display web content.
frame.add(BrowserView.newInstance(browser));
frame.setSize(1280, 800);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
// Load the required web page.
browser.navigation().loadUrl("http://localhost");
//zrx 监听浏览器下载
browser.set(StartDownloadCallback.class, (params, tell) -> {
Download download = params.download();
// Download target details.ddd
DownloadTarget downloadTarget = download.target();
String suggestedFileName = downloadTarget.suggestedFileName();
//System.out.println(downloadTarget.url());
String processName = suggestedFileName.length() > 10 ? suggestedFileName.substring(0, 10) + "..." : suggestedFileName;
//选择文件夹
JFileChooser jfc = new JFileChooser();
jfc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
int flg = jfc.showDialog(new JLabel(), "选择");
// 如果选择了
if (flg == JFileChooser.APPROVE_OPTION) {
ProgressDialog dlgMain = new ProgressDialog(100, frame);
//监听下载
download.on(DownloadUpdated.class, event -> {
// Print download progress in percents.
event.progress().ifPresent(progress -> {
dlgMain.setVisible(true);
JProgressBar progressBar = dlgMain.getProgressBar();
progressBar.setValue((int) progress.value());
progressBar.setString(processName + ":" + progress.value() + "%");
if ((int) progress.value() == 100) {
dlgMain.dispose();
}
}
);
/*long currentSpeed = event.currentSpeed();
long totalBytes = event.totalBytes();
long receivedBytes = event.receivedBytes();*/
});
File file = jfc.getSelectedFile();
// Tell the engine to download and save the file.
tell.download(Paths.get(file.getAbsolutePath(), suggestedFileName));
}
});
});
}
}
如上,核心代码便是 browser.navigation().loadUrl(“http://localhost”),除此之外,在这里我监听了浏览器的下载事件,实现了一个简单的进度条下载提示,默认下载的时候没有提示,不大友好。
整体套壳实现效果(上传和下载)如下:
结语
通过本文,我们在分享云盘系统的同时着重讲解了大文件断点续传的相关知识,捋顺了其中的主要流程,这也是云盘系统的核心功能之一,然后我们也借此了解了 JxBrower 套壳,之后如果有客户端开发的场景都可以作为解决方案备用。希望通过本文能让你有所收获,有所感悟。
想要获取本文所有源码资料的朋友可以关注公众号螺旋编程极客
发送云盘
获取,期待你的关注,我们下次再见!