Netty中文件上传的实现

来源:互联网 发布:犀牛软件 垃圾袋 编辑:程序博客网 时间:2024/06/08 05:12
  1. 技术点描述

    上传大文件并且没有内存压力的文件服务。

  2. 实现方案

    Netty利用块(HttpChunk)对开发文件服务器端做了很好的实现:

    内容存储方式:

        内存、硬盘、混合存储。

  3. 参考源码包

  1. 源码包:

  2. 接口/抽象类/类说明:
  • InterfaceHttpData(interface):实现该接口的对象可以用HttpPostRequestEncoder/Decoder编解码
  • HttpDataFactory(interface):创建InterfaceHttpData对象
  • HttpData(interface):继承InterfaceHttpData
  • Attribute(interface):属性接口,继承HttpData
  • FileUpLoad(interface):可存放在内存、临时文件或一些其他实现
  1. DefaultHttpDataFactory

根据构造函数给FileUpload和Attribute提供默认的工厂。

  • 重要方法:
  1. public DefaultHttpDataFactory(boolean useDisk) {

    this.useDisk = useDisk;

    checkSize = false;

    }

    参数为true时设置为硬盘存储,为false内存存储

  2. public DefaultHttpDataFactory(long minSize) {

    useDisk = false;

    checkSize = true;

    this.minSize = minSize;

    }

    为混合存储模式,如果HttpData大于最小值将会存储在硬盘,否则存储在内存

  • 重要属性

    private final ConcurrentHashMap<HttpRequest, List<HttpData>> requestFileDeleteMap =

    new ConcurrentHashMap<HttpRequest, List<HttpData>>();

    保存所有HttpRequest的数据,直到调用cleanAllHttpDatas()

  1. HttpPostRequestEncoder

    说明:implements ChunkedInput(通过ChunkedWriteHandler传输)。将请求表单编码成POST。

    主要方法:

    HttpRequest finalizeRequest()

    结束request头准备并返回将被发送的request.如果请求不需要分块,只有这一个请求被发送到服务端。

    if (isMultipart) {

    String value = HttpHeaders.Values.MULTIPART_FORM_DATA + "; " +

    HttpHeaders.Values.BOUNDARY + "=" + multipartDataBoundary;

    request.addHeader(HttpHeaders.Names.CONTENT_TYPE, value);

    } else {

    // Not multipart

    request.addHeader(HttpHeaders.Names.CONTENT_TYPE,

    HttpHeaders.Values.APPLICATION_X_WWW_FORM_URLENCODED);

    }

    判断需不需要上传文件并进行设置。

  2. DiskFileUpload

将FileUpload存储到真实文件。

重要属性:

baseDirectory 存储上传文件的目录

deleteOnExitTemporaryFile 在虚拟机推出访问是是否删除文件,默认值为true

  1. Demo实现

    1. HttpUploadClient

bodyRequestEncoder.addBodyAttribute("getform", "POST");

bodyRequestEncoder.addBodyAttribute("info", "first value");

bodyRequestEncoder.addBodyAttribute("secondinfo", "secondvalue&");

bodyRequestEncoder.addBodyAttribute("thirdinfo", textAreaLong);

bodyRequestEncoder.addBodyFileUpload("myfile", file, "application/x-zip-compressed", true);//false 以二进制方式传输,true以文本方式传输

bodyRequestEncoder.addBodyAttribute("Send", "Send");

添加HttpPostRequestEncoder上传的属性和文件

  1. HttpUploadServerHandler

DiskFileUpload.deleteOnExitTemporaryFile = false;

DiskFileUpload.baseDirectory = "D:\\"; DiskAttribute.deleteOnExitTemporaryFile = true; DiskAttribute.baseDirectory = "D:\\";

设置退出时是否删除临时生成的属性文件和上传的文件。

 

public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {

    if (decoder != null) {

decoder.cleanFiles();

}

}

连接关闭时同时清除与此请求相关的文件。

 

if (fileUpload.isCompleted()) {

    fileUpload.renameTo(File dest);//当上传结束时可以将文件写到指定目录,但不建议这样做。

}

以下是renameTo方法的实现

FileInputStream inputStream = new FileInputStream(file);

FileOutputStream outputStream = new FileOutputStream(dest);

FileChannel in = inputStream.getChannel();

FileChannel out = outputStream.getChannel();

int chunkSize = 8196;

long position = 0;

while (position < size) {

if (chunkSize < size - position) {

chunkSize = (int) (size - position);

}

position += in.transferTo(position, chunkSize , out);

}

在测试过程中发现这个方法很耗内存。

可以使用生成的临时文件,要清除requestFileDeleteMap中保存的数据。否则decoder.cleanFiles();会将临时文件删除

decoder.removeHttpDataFromClean(fileUpload);

0 0
原创粉丝点击