软件简介

基于netty的文件上传下载组件

1.简介

该组件基于netty3.6.3实现,具有如下功能:文件上传,文件替换,文件删除,如果是图片的话,还可以生成缩略图等功能。
使用简单,只需要引入netty-file-client,即可以实现文件的以上操作。
该组件的代码结构分为两部分,客户端组件(netty-flie-client)和服务端组件(netty-flie-server)。如下图所示:

 

2.netty-file-client

2.1 概述

客户端组件主要提供对外访问服务端组件的接口,提供以下接口:文件上传,文件替换,文件删除,如果是图片的话,还可以生成缩略图等功能。代码结构如下:

org.lyx.file.client包是该组件的核心包,FileClient类是对外提供接口的工具类。具有以下方法:

  1. uploadFile 文件上传,对应文件处理句柄类为:UploadFileClientHandler

  2. deleteFile 删除服务端文件,对应文件处理句柄类为:DeleteFileClientHandler

  3. replaceFile 替换服务端文件,对应文件处理句柄类为:ReplaceFileClientHandler

  4. createThumbPicture 生成缩略图,对应文件处理句柄类为:CreateThumbPictureClientHandler

以上所有句柄类的父类均为UploadFileClientHandler,该类实现了一些共有方法,比如一些公共参数的包装等。

2.2 实现步骤

实现步骤以上传文件为例,其他类似实现。
直接上代码:

/**
     * 文件上传
     * @param file 需要上传的文件
     * @param fileName 文件名称
     * @param thumbMark 是否需要生成缩略图
     * @return
     * @author:landyChris
     */
    public static String uploadFile(File file, String fileName,
            boolean thumbMark) {
        FileClientPipelineFactory clientPipelineFactory = new FileClientPipelineFactory();
        //辅助类。用于帮助我们创建NETTY服务
        ClientBootstrap bootstrap = createClientBootstrap(clientPipelineFactory);
        String strThumbMark = Constants.THUMB_MARK_NO;
        if (thumbMark) {
            strThumbMark = Constants.THUMB_MARK_YES;
        }
        //具体处理上传文件逻辑
        uploadFile(bootstrap, FileClientContainer.getHost(),
                FileClientContainer.getPort(), file, fileName, strThumbMark,
                FileClientContainer.getUserName(),
                FileClientContainer.getPassword());
        Result result = clientPipelineFactory.getResult();
        if ((result != null) && (result.isCode())) {
            return result.getFilePath();
        }
        return null;
    }

具有三个参数,前面几行代码都是很一些netty的初始化工作,具体看一个私有方法uploadFile,如下代码所示:

private static void uploadFile(ClientBootstrap bootstrap, String host,
            int port, File file, String fileName, String thumbMark,
            String userName, String pwd) {
        //1.构建uri对象
        URI uri = getUri(host, port);
        //2.连接netty服务端
        ChannelFuture future = bootstrap.connect(new InetSocketAddress(host,
                port));
        //3.异步获取Channel对象
        Channel channel = future.awaitUninterruptibly().getChannel();
        if (!future.isSuccess()) {
            future.getCause().printStackTrace();
            bootstrap.releaseExternalResources();
            return;
        }
        //4.初始化文件上传句柄对象
        WrapFileClientHandler handler = new UploadFileClientHandler(host, uri,
                file, fileName, thumbMark, userName, pwd);
        //5.获取Request对象
        HttpRequest request = handler.getRequest();
        //6.获取Http数据处理工厂
        HttpDataFactory factory = getHttpDataFactory();
        //7.进行数据的包装处理,主要是进行上传文件所需要的参数的设置,此时调用的句柄是具体的UploadFileClientHandler对象
        HttpPostRequestEncoder bodyRequestEncoder = handler
                .wrapRequestData(factory);
        //8.把request写到管道中,传输给服务端
        channel.write(request);
        //9.做一些关闭资源的动作
        if (bodyRequestEncoder.isChunked()) {
            channel.write(bodyRequestEncoder).awaitUninterruptibly();
        }
        bodyRequestEncoder.cleanFiles();
        channel.getCloseFuture().awaitUninterruptibly();
        bootstrap.releaseExternalResources();
        factory.cleanAllHttpDatas();
    }

主要有以下实现步骤:

  1. 构建uri对象

  2. 连接netty服务端

  3. 异步获取Channel对象

  4. 初始化文件上传句柄对象

  5. 获取Request对象

  6. 获取Http数据处理工厂

  7. 进行数据的包装处理,主要是进行上传文件所需要的参数的设置,此时调用的句柄是具体的UploadFileClientHandler对象

  8. 把request写到管道中,传输给服务端

  9. 做一些关闭资源的动作
    具体细节实现请参考github上的代码。如果各位读者喜欢的话,可以加个star哈。

3.netty-file-server

3.1 概述

服务端组件实现功能也是跟客户端一致,具有以下功能:文件上传,文件替换,文件删除,如果是图片的话,还可以生成缩略图等功能。代码结构如下图所示:

org.lyx.file.server包是该组件的核心包。具体的处理句柄类有以下几个:

  1. 文件上传:UploadFileServerHandler

  2. 删除文件:DeleteFileServerHandler

  3. 替换文件:ReplaceFileServerHandler

  4. 生成缩略图:CreateThumbPictureServerHandler

以上所以句柄类的接口均为FileServerProcessor,并且继承了抽象类AbstractFileServerHandler。

3.2 实现步骤

具体实现步骤还是以文件上传为例。
首先org.lyx.file.server.support.FileServerHandler类会持续监听客户端的请求,如果是文件处理动作,则会进入messageReceived方法进行相应的处理逻辑。该类定义了以下成员变量:

//http请求
    private HttpRequest request;
    //是否需要断点续传作业
    private boolean readingChunks;
    //接收到的文件内容
    private final StringBuffer responseContent = new StringBuffer();
    //解析收到的文件
    private static final HttpDataFactory factory = new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE); //16384L
    //post请求的解码类,它负责把字节解码成Http请求。
    private HttpPostRequestDecoder decoder;
    //请求参数
    private RequestParam requestParams = new RequestParam();

该方法实现中,如果文件大小小于chunked的最小值,则直接进行文件上传操作。否则,需要进行分块处理。然后进行文件上传操作。
文件大小小于1k的操作:

需要分块处理操作:


以上操作主要有两个注意点:

  1. 请求参数的解析工作(根据HttpDataType进行相应参数的赋值操作)

  2. 根据解析的参数进行相应的文件处理操作(根据文件操作类型,选择相应的处理句柄进行文件处理)

展开阅读全文

代码

的 Gitee 指数为
超过 的项目

评论 (1)

加载中
你好!我在学习使用你提供的demo时,发现有些文件无法上传,具体怎么回事呢?
2017/12/05 17:35
回复
举报
更多评论
暂无内容
发表于软件架构专区
2016/10/08 14:34

[WARNING] Failed to create parent directories for tracking file ...

Jenkins 进行maven项目构建时,遇到 [WARNING] Failed to create parent directories for tracking file 或者还包含[ERROR] maven-compiler-plugin-3.3.pom.part.lock (No such file or directory) -> [Help 1] 这时如果加上输入$WORKSPACE变量会发现目录不对。 解决方法是: 勾选Restrict where this project can be ru...

0
0
发表了博客
2014/03/09 20:54

match_parent和fill_parent

match_parent和fill_parent

0
0
发表了博客
04/05 00:55

match_parent和fill_parent有什么区别? - What is the difference between match_parent and fill_parent?

问题: I'm a little confused about two XML properties: match_parent and fill_parent . 我对两个XML属性有点困惑: match_parent和fill_parent 。 It seems that both are the same. 似乎两者都是一样的。 Is......

0
0
发表了博客
06/10 01:14

match_parent和fill_parent有什么区别? - What is the difference between match_parent and fill_parent?

问题: I'm a little confused about two XML properties: match_parent and fill_parent . 我对两个XML属性有点困惑: match_parent和fill_parent 。 It seems that both are the same. 似乎两者都是一样的。 Is......

0
0
发表了博客
08/10 12:07

match_parent和fill_parent有什么区别? - What is the difference between match_parent and fill_parent?

问题: I'm a little confused about two XML properties: match_parent and fill_parent . 我对两个XML属性有点困惑: match_parent和fill_parent 。 It seems that both are the same. 似乎两者都是一样的。 Is......

0
0
发表了博客
2016/02/01 22:33

fill_parent, wrap_content, match_parent

三个属性都用来适应视图的水平或垂直大小,一个以视图的内容或尺寸为基础的布局比精确地指定视图范围更加方便。 1)fill_parent 设置一个构件的布局为fill_parent将强制性地使构件扩展,以填充布局单元内尽可能多的空间。这跟Windows控件的dockstyle属性大体一致。设置一个顶部布局或控件为fill_parent将强制性让它布满整...

0
0
发表于大前端专区
2018/08/10 14:49

vm.$parent

类型:Vue instance 详细: 父实例,如果当前实例有的话。 这种改法在组件嵌套时,子组件可以直接 修改它的父组件的值 具体使用: 父组件 app.vue <template> <div id="app"> {{ a }} <!-- 使用组件 ...

0
0
发表了博客
2019/05/09 16:37

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)解析

1、https://blog.csdn.net/weixin_41157654/article/details/80820520 最近跟着前辈们打打下手,顺便研究研究代码,以前没怎么用过QT和C++语言,这次跟着前辈好好学习。在研究代码时看到这样的形式,很怪异,就在网上找了一下,发现有个解释的就转载了过来,学习学习,没想到是初始化。 在mianWindow.cpp中第一行代码如下...

0
0
发表了博客
2019/08/19 11:37

oracle树根据parent_id生成parent_ids

oracle 树形结构经常要通过parent_id生成parent_ids(当前节点路径) //根据parent_id生成parent_ids update sys_office set parent_ids = ( SELECT parent_ids from ( SELECT tt.id, TRIM(LEADING ',' FROM SYS_CONNECT_BY_PATH(tt.id, ',')) as parent_ids from SYS_OFFICE tt START WITH tt.parent_id='0' CONNECT BY P...

0
0
发表了博客
2019/03/29 11:59

dependencyManagement、parent与dependencies

本文总结自:https://www.cnblogs.com/feibazhf/p/7886617.html 当我们的项目很多时,为了适应子项目的依赖项使用统一的版本,我们可以创建一个parent项目来管理子项目的公共的依赖。 parent项目根据dependencyManagement来定义各个依赖项的版本 例如: <dependencyManagement> <dependencies> ...

0
0
没有更多内容
加载失败,请刷新页面
点击加载更多
加载中
下一页
暂无内容
1 评论
26 收藏
分享
返回顶部
顶部