您的位置:首页 > 理论基础 > 计算机网络

【Netty4 简单项目实践】五、Netty4接收HTTP文件上传

2016-05-01 23:02 459 查看
又要开一个接收文件上传的服务,找了官方的样例代码,把不需要的东西删了一圈,很容易就实现了。

Bootstrap没什么变化,所以只写上initChannel需要加载的处理器

.childHandler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
// TODO Auto-generated method stub
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new HttpRequestDecoder());
pipeline.addLast(new HttpResponseEncoder());
pipeline.addLast("compressor", new HttpContentCompressor());
pipeline.addLast(new HttpFileHandler());
}
});


然后是HttpFileHandler.java这个超级大的处理器,把import也贴出来了,因为像Cookie类的位置有了变化,可能需要在import里面确认一下来消除warning。

这个处理器的原理是接收HttpObject对象,按照HttpRequest,HttpContent来做处理,文件内容是在HttpContent消息带来的。

在这个样例中,只响应feed的Post请求,其他的请求都被

if (!path.startsWith("/feed") ||request.getMethod().equals(HttpMethod.GET))
滤掉了

针对HttpRequest只做了两件事,构造一个解码器decoder,看请求中是否含multi-part。
初始化静态代码块设置文件下载的特性

static {

DiskFileUpload.deleteOnExitTemporaryFile =true;
// should delete file

// on exit (in normal

// exit)

DiskFileUpload.baseDirectory =
null; // system temp directory

DiskAttribute.deleteOnExitTemporaryFile =true;
// should delete file on

// exit (in normal exit)

DiskAttribute.baseDirectory =
null; // system temp directory

}
大意就是下载后删除缓存文件,中断的话删除缓存文件,采用默认的缓存目录。

然后在HttpContent中一个chunk一个chunk读吧,读到LastHttpContent对象处理完之后返回即可。

HttpFileHandler.java

/**
* 接收HTTP文件上传的处理器
*/
import static io.netty.buffer.Unpooled.copiedBuffer;
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH;
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;
import static io.netty.handler.codec.http.HttpHeaders.Names.COOKIE;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.Collections;
import java.util.Set;

import org.json.JSONObject;

import com.seeplant.util.Property;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpObject;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.codec.http.cookie.Cookie;
import io.netty.handler.codec.http.cookie.ServerCookieDecoder;
import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory;
import io.netty.handler.codec.http.multipart.DiskAttribute;
import io.netty.handler.codec.http.multipart.DiskFileUpload;
import io.netty.handler.codec.http.multipart.FileUpload;
import io.netty.handler.codec.http.multipart.HttpDataFactory;
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder;
import io.netty.handler.codec.http.multipart.InterfaceHttpData;
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.EndOfDataDecoderException;
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.ErrorDataDecoderException;
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.IncompatibleDataDecoderException;
import io.netty.handler.codec.http.multipart.InterfaceHttpData.HttpDataType;
import io.netty.util.CharsetUtil;

public class HttpFileHandler extends SimpleChannelInboundHandler<HttpObject>{
private HttpRequest request;
private boolean readingChunks;
private final StringBuilder responseContent = new StringBuilder();

private static final HttpDataFactory factory =
new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE); // Disk if size exceed

private HttpPostRequestDecoder decoder;

static {
DiskFileUpload.deleteOnExitTemporaryFile = true; // should delete file
// on exit (in normal
// exit)
DiskFileUpload.baseDirectory = null; // system temp directory
DiskAttribute.deleteOnExitTemporaryFile = true; // should delete file on
// exit (in normal exit)
DiskAttribute.baseDirectory = null; // system temp directory
}

@Override
protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
// TODO Auto-generated method stub
JSONObject jObject = new JSONObject().put("code", "404").put("msg", "page not found");

if (msg instanceof HttpRequest) {
this.request = (HttpRequest) msg;
URI uri = new URI(request.getUri());
String path = uri.getPath();

setCookie(this.request);

/* url区分的入口在这里 */
if (!path.startsWith("/feed") || request.getMethod().equals(HttpMethod.GET)) {
writeResponseString(ctx, jObject.toString());
return;
} else {
try {
decoder = new HttpPostRequestDecoder(factory, request);
} catch (ErrorDataDecoderException e1) {
e1.printStackTrace();
responseContent.append(e1.getMessage());
writeResponseString(ctx, jObject.toString());
ctx.channel().close();
return;
} catch (IncompatibleDataDecoderException e1) {
// 既然Get不需要创建解码器,自己实现业务的时候,流程不要走在这里面
// GET Method: should not try to create a HttpPostRequestDecoder
// So OK but stop here
writeResponseString(ctx, jObject.toString());
return;
}

readingChunks = HttpHeaders.isTransferEncodingChunked(request);
//这里可以看到是否含有上传文件部分
if (readingChunks) {
// Chunk version
readingChunks = true;
}
}
} else if (msg instanceof HttpContent) {
if (decoder != null && readingChunks) {
HttpContent chunk = (HttpContent) msg;
try{
decoder.offer(chunk);
} catch (ErrorDataDecoderException e1) {
writeResponseString(ctx, jObject.toString());
reset();
ctx.channel().close();
return;
}
readHttpDataChunkByChunk(); //从解码器decoder中读出数据
if (chunk instanceof LastHttpContent) {
writeResponseString(ctx, "{\"code\":0,\"msg\":\"ok\"}");
readingChunks = false;
reset();
}
} else {
writeResponseString(ctx, jObject.toString());
ctx.channel().close();
return;
}
}
}

/**
* Example of reading request by chunk and getting values from chunk to chunk
* 从decoder中读出数据,写入临时对象,然后写入...哪里?
* 这个封装主要是为了释放临时对象
*/
private void readHttpDataChunkByChunk() {
try {
while (decoder.hasNext()) {
InterfaceHttpData data = decoder.next();
if (data != null) {
try {
// new value
writeHttpData(data);
} finally {
data.release();
}
}
}
} catch (EndOfDataDecoderException e1) {
// end
responseContent.append("\r\n\r\nEND OF CONTENT CHUNK BY CHUNK\r\n\r\n");
}
}

/**
* 设置cookie
*/
private void setCookie(HttpRequest request) {
Set<Cookie> cookies;
String value = request.headers().get(COOKIE);
if (value == null) {
cookies = Collections.emptySet();
} else {
cookies = ServerCookieDecoder.LAX.decode(value);
}
for (Cookie cookie : cookies) {
responseContent.append("COOKIE: " + cookie + "\r\n");
}
responseContent.append("\r\n\r\n");
}
/**
* 封装应答的回写
* @param ctx
* @param message String的消息体Header中已经设置为Application/json
*/
private void writeResponseString(ChannelHandlerContext ctx, String message) {
// Convert the response content to a ChannelBuffer.
responseContent.setLength(0);
responseContent.append(message);

ByteBuf buf = copiedBuffer(responseContent.toString(), CharsetUtil.UTF_8);
// Build the response object.
FullHttpResponse response = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1, HttpResponseStatus.OK, buf);

response.headers().set(CONTENT_TYPE, "application/json; charset=UTF-8");
response.headers().set(CONTENT_LENGTH, buf.readableBytes());

// Write the response.
ctx.channel().writeAndFlush(response);
}

private void reset() {
request = null;

// destroy the decoder to release all resources
decoder.destroy();
decoder = null;
}

private void writeHttpData(InterfaceHttpData data) {
// Attribute就是form表单里带的各种 name= 的属性
if (data.getHttpDataType() == HttpDataType.Attribute) {
} else if (data.getHttpDataType() == HttpDataType.InternalAttribute){
}else{
String uploadFileName = getUploadFileName(data);
FileUpload fileUpload = (FileUpload) data;
if (fileUpload.isCompleted()) {
// fileUpload.isInMemory();// tells if the file is in Memory
// or on File
// fileUpload.renameTo(dest); // enable to move into another
// File dest
// decoder.removeFileUploadFromClean(fileUpload); //remove
// the File of to delete file
File dir = new File(Property.getSaveFileDir() + "download" + File.separator);
if (!dir.exists()) {
dir.mkdir();
}
File dest = new File(dir, uploadFileName);
try {
fileUpload.renameTo(dest);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}

private String getUploadFileName(InterfaceHttpData data) {
String content = data.toString();
String temp = content.substring(0, content.indexOf("\n"));
content = temp.substring(temp.lastIndexOf("=") + 2, temp.lastIndexOf("\""));
return content;
}
}


帮助类Property.java

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

public class Property {
private static Properties properties = new Properties();
static {
File file = new File(System.getProperty("user.dir")+"setting.properties");
if (!file.exists())
{
file = new File(Property.class.getResource("/").getPath()+"setting.properties");
}

try (FileInputStream fis =
new FileInputStream(file)){
properties.load(fis);
} catch (IOException e) {
// TODO Auto-generated catch block

e.printStackTrace();
}
}

private Property(){
}

public static String getProperty(String key) {
return properties.getProperty(key);
}

public static String getSaveFileDir(){
return System.getProperty("user.dir") + File.separator;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: