问题起源
对于多数开发者而言,可能都希望能将自己的程序中定时产生一些重要文件(例如运行日志、爬虫结果、统计报表等)自动定时上传至一个程序内置的私有网络文件共享平台上,并在上传时按照上传日期或者其版本号命名。这样一来,就可以实现用户在任意位置进行访问和下载这些文件了——相比于远程登录到程序运行的计算机内再进行拷贝而言,如此设计当然要高效得多(只要用户有浏览器即可),操作过程也会更简单。
那么要如何才能实现这一功能呢?本文就来介绍一种通过Java程序配合kiftd开源网盘的方式,来实现上述功能(这里简单介绍一下kiftd:它是一款轻量化的开源网盘,能够用来在各个操作系统上搭建私有云盘或局域网网盘)。
注:本教程所有截图均为实际演示截图,且提供的代码均经过实际测试和检验,请放心阅读。
1,部署好kiftd并进行设置:
首先,我们需要先在本地安装好一份kiftd。安装过程十分简单:首先,前往kiftd的官网免费下载一份zip压缩包;之后,将其解压至硬盘中的任意位置(路径中最好不要有中文)。
在本示例中,我们将kiftd解压到
路径下(后文中均使用该位置作为kiftd的路径)。
接下来,需要对kiftd进行一些配置。请双击解压目录中的jar程序启动kiftd【标号1】,并在设置窗口中【标号2】将其端口号设置为9000【标号3】(注意:该端口号可以随意取,但不能与自己运行的其他程序重复,例如自己运行的Tomcat如果使用了8080端口,那么kiftd就必须换成其它的端口号),如图所示:
现在kiftd的设置工作便已经完成了,也就能够正常使用了。
提示:对于无图形化界面的服务器操作系统及Linux系统而言,也可以通过编辑conf文件夹中的server.properties文件并修改port=8080一项的方式来设置端口,效果与上述方法相同。
2,使用自己的Java程序来开启kiftd网盘并访问
我们需要用自己的Java程序去控制kiftd的启动,以便定期向其中导入一些文件供用户访问和下载。
这里使用的方式便是使用Java自带的
对象实现该功能,它能够以独立的进程运行控制台/终端命令。我们先关闭kiftd,之后在自己的程序中使用Runtime类的exec方法开启kiftd,该kiftd将在一个单独的JVM中启动并运行(与我们自己的Java程序相互独立):
Process p = Runtime.getRuntime().exec("java -jar /Users/kohgylw/program/kiftd-test/kiftd-1.0.17-SNAPSHOT.jar -console");
注意,上述命令中的jar程序所在路径要自行替换,且在Windows系统下应该使用“\\”分割,Linux和Mac OS X下则是“/”(本示例中的情况)。执行完上述代码后,kiftd将以“命令模式”启动并等待进步一的操作指令(关于该命令的具体说明请参见kiftd官方提供的《kiftd说明文档》)。
此时,kiftd已经准备就绪,但还未启动kiftd的服务器。为了能够开始访问,我们需要【继续使用】该Process对象来向kiftd进程发送“-start”命令,用以开启kiftd的服务器。示例代码如下:
// 向该进程写入命令,与在终端中操作一致
PrintWriter writer = new PrintWriter(p.getOutputStream());
// 启动服务器,此时会开启kiftd服务器
writer.println("-start");
writer.flush();
上述命令在执行过程中会阻塞线程,直到kiftd服务器开启成功。命令执行完毕后,你就可以打开Chrome或FireFox浏览器访问kiftd的主页了。为了测试,可以在浏览器中输入http://{自己的IP地址}:{端口号}/(例如http://127.0.0.1:9000/),如果kiftd启动成功的话,你会在浏览器中看到下图所示的效果(本人的kiftd中已经存储了一些文件,如果你的kiftd是新安装的,那么文件列表也应该是空的。默认的管理员账户是admin,密码000000):
3,使用Java程序向kiftd中定时存入文件
现在,回到我们最初的目的上——我们希望能够通过程序控制,自动将一些文件上传到kiftd中供用户下载。为了模拟这一需求,现在在
路径下建立一个文件夹,并将其命名为
,如图所示:
假设该文件夹将会不定期地生成一些文件在其中,那么我们就希望能够通过程序来控制任何存入该文件夹的文件都能自动上传至kiftd的ROOT路径下。
为了实现这一效果,我们继续使用上面得到的PrintWriter对象向kiftd线程发送“-files”指令,进入到kiftd的文件管理模式:
// 进入命令模式中的文件管理功能
writer.println("-files");
writer.flush();
在该模式下,kiftd将接收文件处理指令,例如我们需要用到的导入指令。“导入指令”可以实现将本地文件(或文件夹)导入至kiftd中(初始为/ROOT/根路径,可以使用cd命令进入其他位置),其命令格式为:
import {要导入的本地文件(必须使用完整路径)}
接下来,我们为上文中创建的uploads文件夹添加一个路径监听器来监听其中的文件增加事件,这里使用了JDK 1.7中提供的WatchService工具类,该类能够监听uploads文件夹内的文件创建事件(无论是新建文件还是将其他文件拷贝到该文件夹内,都统一算作文件创建事件),示例代码如下:
//开启一个独立线程监听这个文件夹内的文件创建
Thread t = new Thread(() -> {
while (true) {
try {
//通过监听路径获取一个Path对象
Path path = new File("/Users/kohgylw/program/uploads/").toPath();
//由文件系统得到一个监听服务
WatchService ws = path.getFileSystem().newWatchService();
//声明监听文件创建事件(这里也可以监听文件夹),每个监听服务只能监听一次动作,因此需要循环监听
path.register(ws, StandardWatchEventKinds.ENTRY_CREATE);
//开始监听并得到结果,当指定事件发生时,该方法将放行并继续执行后续过程
WatchKey wk = ws.take();
//得到事件列表
List<WatchEvent<?>> es = wk.pollEvents();
for (WatchEvent<?> we : es) {
//得到相应的文件创建事件
if (we.kind() == StandardWatchEventKinds.ENTRY_CREATE) {
//得到触发该事件的文件名称
String newfile=we.context().toString();
//向kiftd发送导入命令来导入该文件:
writer.println("import /Users/kohgylw/program/uploads/"+newfile);
}
}
} catch (Exception e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
});
t.start();
现在,我们就可以启动我们的程序了,可以看到当前uploads文件夹内空无一物:
接下来,我们在其中存入一个名为1.txt的文本文件,如图所示:
稍等片刻后(因为文件改动监听器有一定的延迟),我们回到网盘主页并刷新文件列表。可以看到,此时“1.txt”文件已经被自动上传至文件列表中,并能够供用户下载了:
继续试验:如果我们再在uploads中创建一个新的文本文件“2.txt”,如图所示:
之后,我们回到浏览器中刷新kiftd的文件列表,可以看到“2.txt”也已经被自动导入到了文件列表中:
从上述示例中可以看出,此时任何新的文件出现在uploads文件夹后都会被自动发布至kiftd网盘上。那么之后,我们就可以设计让自己的程序将自动生成的文件全部保存至该路径下,从而实现自动发布了。导入成功后的文件无需继续留在该文件夹内,可以直接删除,因为kiftd会在自己的文件系统中独立保存它(而不是直接使用本地文件)。
至此,我们便实现了将自动生成的文件上传至网盘并供用户下载的需求。如此一来,自己程序中产生的日志文件、爬虫结果文件或是其他什么动态生成的文件均能够自动发布至kiftd网盘上,方便用户在任意地点浏览和下载它们——整个过程可以实现完全的自动化运行,无需人为干预。
4,使用Java代码控制kiftd的关闭
在程序中我们可能还需要在不需要kiftd时关闭它(例如让kiftd能够随程序关闭一同关闭)。需要实现这一功能也很简单,只需:
1,继续使用PrintWriter对象向kiftd进程发送“exit”指令退出文件管理模式(回到控制台);
2,然后再向其发送“-exit”指令来关闭整个kiftd
即可。示例代码如下:
//控制kiftd关闭,首先需要发送exit指令退出文件管理模式
writer.println("exit");
writer.flush();
//再发送-exit指令关闭整个kiftd
writer.println("-exit");
writer.flush();
结语
以上就是利用kiftd配合Java的执行控制台/终端命令的功能来实现自动生成的文件能够自动分享至私有云盘功能的全部内容。当然,本文展示的仅仅是最简单的操作方法,此外还有很多其他的手段也能实现类似的功能(本文就不再做讨论)。如果你要实现更加复杂的功能(例如文件定时备份、自动覆盖旧版本文件、分权限管理文件等),那么还需要进一步研究kiftd的更多命令操作。更多命令可以参见官方提供的《kiftd说明文档》,介绍非常详尽,其执行的原理与本文中提到的方式都相同。大家可以根据自身需要灵活进行修改和扩展,以实现更加丰富的功能。
最后,整个示例程序的代码已经放在下面了,如有需要可直接拷贝:
package kohgylw;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.nio.file.Path;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.List;
public class MC {
public static void main(String[] args) {
try {
// 使用该命令启动kiftd,其中kiftd的路径需要替换为您的安装路径
Process p = Runtime.getRuntime()
.exec("java -jar /Users/kohgylw/program/kiftd-test/kiftd-1.0.17-SNAPSHOT.jar -console");
//这个是输出kiftd反馈信息的方法,仅用于进行测试,在实际使用中可以不调用。
print(p);
// 向该进程写入命令,与在终端中操作一致
PrintWriter writer = new PrintWriter(p.getOutputStream());
// 启动服务器,此时会开启kiftd服务器
writer.println("-start");
writer.flush();
// 进入命令模式中的文件管理功能
writer.println("-files");
writer.flush();
// 开启一个独立线程监听这个文件夹内的文件创建
Thread t = new Thread(() -> {
while (true) {
try {
// 通过监听路径获取一个Path对象
Path path = new File("/Users/kohgylw/program/uploads/").toPath();
// 由文件系统得到一个监听服务
WatchService ws = path.getFileSystem().newWatchService();
// 声明监听文件创建事件(这里也可以监听文件夹),每个监听服务只能监听一次动作,因此需要循环监听
path.register(ws, StandardWatchEventKinds.ENTRY_CREATE);
// 开始监听并得到结果,当指定事件发生时,该方法将放行并继续执行后续过程
WatchKey wk = ws.take();
// 得到事件列表
List<WatchEvent<?>> es = wk.pollEvents();
for (WatchEvent<?> we : es) {
// 得到相应的文件创建事件
if (we.kind() == StandardWatchEventKinds.ENTRY_CREATE) {
// 得到触发该事件的文件名称
String newfile = we.context().toString();
// 向kiftd发送导入命令来导入该文件:
writer.println("import /Users/kohgylw/program/uploads/" + newfile);
writer.flush();
}
}
} catch (Exception e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
});
t.start();
//控制kiftd关闭,首先需要发送exit指令退出文件管理模式
// writer.println("exit");
// writer.flush();
//再发送-exit指令关闭整个kiftd
// writer.println("-exit");
// writer.flush();
} catch (Exception e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
//用于打印kiftd产生的反馈信息,仅用于调试,实际使用中可以不调用
public static void print(Process p) {
Thread t = new Thread(() -> {
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = null;
try {
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
});
t.start();
}
}
如果该文章能够帮助到您,欢迎分享!