Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save evvil/1615a0addbe8700ce2e5c20413c6a51f to your computer and use it in GitHub Desktop.
Save evvil/1615a0addbe8700ce2e5c20413c6a51f to your computer and use it in GitHub Desktop.
Netty Http File Upload
package my.netty.http.upload;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
/**
* Class for initializing HTTP Handlers.
*/
final class HttpUploadServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(final SocketChannel socketChannel) throws Exception {
final ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast(new HttpRequestDecoder());
pipeline.addLast(new HttpResponseEncoder());
pipeline.addLast(new MyHttUploadServerHandler());
}
}
package my.netty.http.upload;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
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.HttpHeaderNames;
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.LastHttpContent;
import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory;
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.util.CharsetUtil;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.nio.channels.FileChannel;
import static io.netty.handler.codec.http.HttpMethod.POST;
import static io.netty.handler.codec.http.HttpResponseStatus.CREATED;
import static io.netty.handler.codec.http.HttpResponseStatus.METHOD_NOT_ALLOWED;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
/**
* Handles uploading of file and then saves it to a known location.
*/
public class MyHttUploadServerHandler extends SimpleChannelInboundHandler<HttpObject> {
// Factory that writes to disk
private static final HttpDataFactory factory = new DefaultHttpDataFactory(true);
private static final String FILE_UPLOAD_LOCN = "/tmp/uploads/";
private HttpRequest httpRequest;
private HttpPostRequestDecoder httpDecoder;
@Override
protected void channelRead0(final ChannelHandlerContext ctx, final HttpObject httpObject)
throws Exception {
if (httpObject instanceof HttpRequest) {
httpRequest = (HttpRequest) httpObject;
final URI uri = new URI(httpRequest.uri());
System.out.println("Got URI " + uri);
if (httpRequest.method() == POST) {
httpDecoder = new HttpPostRequestDecoder(factory, httpRequest);
httpDecoder.setDiscardThreshold(0);
} else {
sendResponse(ctx, METHOD_NOT_ALLOWED, null);
}
}
if (httpDecoder != null) {
if (httpObject instanceof HttpContent) {
final HttpContent chunk = (HttpContent) httpObject;
httpDecoder.offer(chunk);
readChunk(ctx);
if (chunk instanceof LastHttpContent) {
resetPostRequestDecoder();
}
}
}
}
private void readChunk(ChannelHandlerContext ctx) throws IOException {
while (httpDecoder.hasNext()) {
InterfaceHttpData data = httpDecoder.next();
if (data != null) {
try {
switch (data.getHttpDataType()) {
case Attribute:
break;
case FileUpload:
final FileUpload fileUpload = (FileUpload) data;
final File file = new File(FILE_UPLOAD_LOCN + fileUpload.getFilename());
if (!file.exists()) {
file.createNewFile();
}
System.out.println("Created file " + file);
try (FileChannel inputChannel = new FileInputStream(fileUpload.getFile()).getChannel();
FileChannel outputChannel = new FileOutputStream(file).getChannel()) {
outputChannel.transferFrom(inputChannel, 0, inputChannel.size());
sendResponse(ctx, CREATED, "file name: " + file.getAbsolutePath());
}
break;
}
} finally {
data.release();
}
}
}
}
/**
* Sends a response back.
* @param ctx
* @param status
* @param message
*/
private static void sendResponse(ChannelHandlerContext ctx, HttpResponseStatus status, String message) {
final FullHttpResponse response;
String msgDesc = message;
if (message == null) {
msgDesc = "Failure: " + status;
}
msgDesc += " \r\n";
final ByteBuf buffer = Unpooled.copiedBuffer(msgDesc, CharsetUtil.UTF_8);
if (status.code() >= HttpResponseStatus.BAD_REQUEST.code()) {
response = new DefaultFullHttpResponse(HTTP_1_1, status, buffer);
} else {
response = new DefaultFullHttpResponse(HTTP_1_1, status, buffer);
}
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8");
// Close the connection as soon as the response is sent.
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
private void resetPostRequestDecoder() {
httpRequest = null;
httpDecoder.destroy();
httpDecoder = null;
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("Got exception " + cause);
ctx.channel().close();
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
if (httpDecoder != null) {
httpDecoder.cleanFiles();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment