又要开一个接收文件上传的服务,找了官方的样例代码,把不需要的东西删了一圈,很容易就实现了。
Bootstrap没什么变化,所以只写上initChannel需要加载的处理器
.childHandler(new ChannelInitializer() {
@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());
}
});
这个处理器的原理是接收HttpObject对象,按照HttpRequest,HttpContent来做处理,文件内容是在HttpContent消息带来的。
在这个样例中,只响应feed的Post请求,其他的请求都被
if (!path.startsWith("/feed") ||request.getMethod().equals(HttpMethod.GET))
滤掉了
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
}
大意就是下载后删除缓存文件,中断的话删除缓存文件,采用默认的缓存目录。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{
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 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;
}
}
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;
}
}