Java Web 文件服务器实现

一、简介

1.简介

之前公司用树莓派做影像处理,当时自己也买了一个玩玩,但是因为最近没在搞影像相关的东西,所以就没咋碰过,吃灰挺长时间了。本着买了就得用,不浪费的精神,搞个文件服务器做文件备份使用。于是开发了这个工具,写下这篇博客记录一下其中知识点。

项目使用 natapp 内网击穿工具暴露到公网,使公网可以访问到是树莓派的本地网络,具体使用方式可以到 natapp官网 上查看。项目整体功能比较简单,就是文件和文件夹的增删改查。但是有些技术细节还是要记录下来做。工具一共实现了六个接口,分别是:文件上传,文件删除,创建文件夹,删除文件夹,查询目录,递归查询文件夹结构。这六个接口就可以满足基本的文件增删改查操作。

2.项目环境:

SpringBoot 2.3.0.RELEASE

Apache commons io:Commons IO is a library of utilities to assist with developing IO functionality.

二、natapp使用

 内网穿透简单来说就是将内网外网通过natapp隧道打通,让内网的数据让外网可以获取。比如常用的办公室软件等,一般在办公室或家里,通过拨号上网,这样办公软件只有在本地的局域网之内才能访问,那么问题来了,如果是手机上,或者公司外地的办公人员,如何访问到办公软件呢?这就需要natapp内网穿透工具了。运行natapp隧道之后,natapp会分配一个专属域名/端口,办公软件就已经在公网上了,在外地的办公人员可以在任何地方愉快的访问办公软件了

内网穿透可以做什么?

1.     上文举例的办公软件

2.     放在家里的树莓派,服务器等,需要远程ssh管理,这样打通服务器的22端口即可远程通过ssh操作服务器了.

3.     微信/支付宝等本地开发.现在微信/支付宝等应用,需要服务器接收微信/支付宝发送的回调信息,然而在本地开发程序的话,还得实时上传到服务器,以便支持微信/支付宝的回调信息,如果使用了natapp内网穿透软件,将回调地址设置成natapp提供的地址,回调数据立即传递回本地,,这样很方便的在本地就可以实时调试程序,无须再不断上传服务器等繁琐且无意义的步骤.

4.     一些企业内部数据库,由于安全等原因,不愿意放到云服务器上,可以将数据库放到办公室本地,然后通过natapp的tcp隧道映射,这样既保证安全,又保证公网可以正常访问.

5.     一些开发板做的监控等信息,每台设备运行一条隧道,可以方便的管理监控各个设备的运行情况.

6.     一些本地运行的游戏,想和好基友一起联网玩,一条命令运行natapp即可实现联网游戏.

7.     群辉上运行natapp之后,随时随地在任何地方可以访问到群辉上应用

以上内容来自官网的介绍,官网地址:https://natapp.cn/    文档地址:https://natapp.cn/article

三、接口设计及实现

1、上传

基础性功能接口,使用  MultipartFile 实现后台文件接收,。核心代码如下代码。

/*创建空白文件*/
File file = new File("target path")
/*从流中读取内容复制到文件中*/
FileUtils.copyInputStreamToFile(multipartFile.getInputStream(), file);

这里用到了 Apache commons-io 工具包,FileUtil.copyInputStreamToFile() 方法将流直接转化为文件实现文件存储。

2、下载

使用输入输出流实现,这里要注意响应头的设置,我在项目中使用到的响应头有如下几个

设置响应编码,及相应类型
response.setCharacterEncoding("utf-8");
response.setContentType("multipart/form-data");
设置下载文件名称
response.setHeader("Content-Disposition", "attachment;fileName=" + fileName);
设置下载文件长度
response.setHeader("Content-Length",inputStream.available() + "");

其他响应头设置可以参考 菜鸟教程HTTP 响应头信息

完整代码如下:

    public void downLoad(@RequestParam(value = "filePath") String filePath,
                         HttpServletResponse response) throws IOException {
        if (!new File(filePath).exists()) {
            response.setHeader("isSuccess","false");
            return;
        }
        File file = new File(config.getBasepath() + filePath);
        FileInputStream inputStream = new FileInputStream(file);
        String fileName = file.getName();
        /*设置响应编码*/
        response.setCharacterEncoding("utf-8");
        /*设置响应类型*/
        response.setContentType("multipart/form-data");
        /*设置下载文件名称*/
        response.setHeader("Content-Disposition", "attachment;fileName=" + fileName);
        /*设置响应头,通知浏览器文件大小*/
        response.setHeader("Content-Length", inputStream.available() + "");
        /*设置自定义响应头,通知浏览器下载触发成功*/
        response.setHeader("isSuccess", "true");
        OutputStream os = response.getOutputStream();
        byte[] buffer = new byte[1024];
        int len = -1;
        while ((len = inputStream.read(buffer)) > 0) {
            os.write(buffer, 0, len);
        }
    }

 

3、创建新文件夹

接收文件路径参数,根据参数创建文件夹。使用 File.mkdirs()实现

4、删除文件或文件夹

接收文件路径参数,根据路径来删除文件或文件夹。使用 File.delete()实现

5、文件夹结构查询

单层文件结构查询,入参为文件夹路径,输出为该文件下文件结构,包括所有文件,所有文件夹。这个方法获取到的文件夹不具备递归结构。

public AjaxResult getMenuWithFile(String path) {
        File file = new File(config.getBasepath(), path);
        if (!file.exists() || file.isFile()) {
            return AjaxResult.getNo("文件夹不存在!");
        }
        /*注意以下两行代码,区分了文件和文件夹*/
        List<File> dirList = Arrays.asList(file.listFiles(pathname -> pathname.isDirectory()));
        List<File> fileList = Arrays.asList(file.listFiles(pathname -> pathname.isFile()));

        Folder2 folder = new Folder2(fileList, dirList);
        return AjaxResult.getOk(folder);
    }

上面代码需要注意的是文件夹和文件的区分,上面代码使用的是lambda表达式,不够直观下面就文件的的过滤展开说明

/*使用lambda表达式*/
File[] files = file.listFiles(pathname -> pathname.isFile())

/*使用匿名对象*/
File[] files = file.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                return pathname.isFile();
            }
        });

file.listFile(FileFilter filtter) 这个方法可以接收FileFilter参数,这个接口是用来做文件过滤的。这里可以自定义过滤规则,返回true则文件会被保留,false文件会被过滤掉。如果想使用文件名做过滤,可以直接传入 FileNameFilter 编写自定义规则。

6、递归查询文件夹结构

与上面方法不同,这个方法可以查询出指定文件夹下的所有文件和文件夹,其中子文件夹是可以展开的,里面还包括所有文件和文件夹。这里使用到了自定义的类。

public class Folder {

    private String name = "";

    /*文件夹下的子文件夹*/
    private List<Folder> folderList = new ArrayList<>();

    /*文件夹下所有文件*/
    private List<File> fileList = new ArrayList<>();

    /*文件夹是否为空*/
    private boolean isEmpty;

    /*省略getter、setter和toString*/

}

实现逻辑:

public Folder traverseFolderRecursive(String path) {
        Folder folder = new Folder();
        File file = new File(path);
        folder.setName(file.getName());
        if (file.exists()) {
            File[] files = file.listFiles();
            if (null == files || files.length == 0) {
//              文件夹为空
                folder.setEmpty(true);
            } else {
                for (File childFile : files) {
                    if (childFile.isDirectory()) {
//                      File为文件夹
                        Folder childFolder = traverseFolderRecursive(childFile.getAbsolutePath());
                        folder.addFolder(childFolder);
                    } else {
//                      file为文件
                        folder.addFile(childFile);
                    }
                }
            }
        } else {
            return null;
        }
        return folder;
    }

四、问题

1.前端文件结构展示

需要找一个前端文件展示的模板,展示服务器文件夹结构。

五、接下来的任务

1.目前基础功能的后台接口已经可以正常使用,接下需要进行一段时间的测试。

2.项目前端需要开发。

3.身份认证,暴露在公网中的服务器必要的功能。

4.断点续传,如果网络存在不稳定中断了下载,那么在进入的时候可以接着上次的任务继续下载(这块我觉得应该是个大坑,还没踩过=.=)。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值