netty簡單實現檔案上傳接收 (OSS物件倉庫) 基於Http 協議.
阿新 • • 發佈:2019-02-10
1.gradle配置(mvn童鞋自行分解)讓其支援json :
compile group: 'io.netty', name: 'netty-all', version: '4.1.9.Final'
compile 'com.google.code.gson:gson:2.8.2'2.
2.構建netty檔案服務主類Server:
import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; 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; import io.netty.handler.stream.ChunkedWriteHandler; import java.net.InetSocketAddress; public class Server { public static void main(String[] args) { server(); } private static void server() { NioEventLoopGroup bossGroup = new NioEventLoopGroup(); NioEventLoopGroup workGroup = new NioEventLoopGroup(); ServerBootstrap bootstrap = new ServerBootstrap(); try { bootstrap.group(bossGroup, workGroup) .channel(NioServerSocketChannel.class) .localAddress(new InetSocketAddress(Common.PORT))//繫結監聽的本地埠號 .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline() .addLast(new HttpRequestDecoder()) .addLast(new HttpResponseEncoder()) .addLast(new ChunkedWriteHandler()) .addLast(new HttpFileServerHandler()); } }); ChannelFuture future = bootstrap.bind().sync(); future.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } finally { bossGroup.shutdownGracefully(); workGroup.shutdownGracefully(); } } }
3.實現檔案路徑管理類PathUtil(對win平臺和Linux(mac)平臺分別支援):
import java.io.File; public class PathUtil { private static final ClassLoader classLoader = PathUtil.class.getClassLoader(); public static String getImageBasePath() { String os = System.getProperty("os.name"); String basePath; if (os.toLowerCase().startsWith("win")) { basePath = "C:/Users/你的win系統使用者名稱路徑/warehouse"; } else { basePath = "/home/workspace/warehouse"; } basePath = basePath.replace("/", File.separator); return basePath; } public static String getSourcePath(String name) { return classLoader.getResource(name).getPath(); } }
4.得到簡單的Gson例項:
import com.google.gson.Gson;
public class GsonUtil {
private static Gson gson;
public static Gson getGson() {
if (gson == null) {
gson = new Gson();
}
return gson;
}
}
5.ResponseModel<T> pojo類 (回執類):
import java.util.Date; public class ResponseModel<T> { //成功 public static final int SUCCEED = 1; //未知錯誤 public static final int ERROR_UNKNOWN = 0; //錯誤的請求 public static final int BAD_REQUEST = 4041; //驗證錯誤 public static final int ERROR_NO_PERMISSION = 2010; //伺服器錯誤 public static final int SERVICE_ERROR = 2010; private int code; private String message; private long time = new Date().getTime(); private T result; public ResponseModel(T result) { this(); this.result = result; } public ResponseModel() { message = "OK"; code = SUCCEED; } public ResponseModel(int code, String message) { this.code = code; this.message = message; } public static <T> ResponseModel<T> buildOk() { return new ResponseModel<T>(); } public static <T> ResponseModel<T> buildOk(T result) { return new ResponseModel<T>(result); } public static <M> ResponseModel<M> buildNoPermissionError() { return new ResponseModel<M>(ERROR_NO_PERMISSION, "你沒有操作許可權!"); } public static <M> ResponseModel<M> buildBadRequestError() { return new ResponseModel<M>(BAD_REQUEST, "錯誤的請求!"); } public static <M> ResponseModel<M> buildOtherError() { return new ResponseModel<M>(ERROR_UNKNOWN, "其他錯誤!"); } public static <M> ResponseModel<M> buildServiceError() { return new ResponseModel<M>(SERVICE_ERROR, "伺服器錯誤!"); } public boolean success() { return code == SUCCEED; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public long getTime() { return time; } public void setTime(long time) { this.time = time; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getResult() { return result; } public void setResult(T result) { this.result = result; } }
6.檔案存放Handler-HttpFileServerHandler:
import com.sun.istack.internal.NotNull;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.multipart.*;
import io.netty.handler.stream.ChunkedFile;
import io.netty.util.CharsetUtil;
import javax.activation.MimetypesFileTypeMap;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.URI;
import java.net.URISyntaxException;
import static io.netty.buffer.Unpooled.copiedBuffer;
import static io.netty.handler.codec.http.HttpResponseStatus.*;
@ChannelHandler.Sharable
public class HttpFileServerHandler extends SimpleChannelInboundHandler<HttpObject> {
private static final String BASE_PATH = PathUtil.getImageBasePath();
private ChannelHandlerContext ctx;
private HttpRequest request;
private static String KEY = "YOU_KEY";
private static final HttpDataFactory factory =
new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE);
private HttpPostRequestDecoder decoder;
static {
DiskFileUpload.deleteOnExitTemporaryFile = true;
DiskFileUpload.baseDirectory = null;
DiskAttribute.deleteOnExitTemporaryFile = true;
DiskAttribute.baseDirectory = null;
}
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
super.channelRegistered(ctx);
System.out.println("channelRegistered");
}
@Override
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
if (decoder != null) {
decoder.cleanFiles();
}
System.out.println("channelUnregistered");
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, HttpObject httpObject) throws Exception {
this.ctx = ctx;
if (httpObject instanceof HttpRequest) {
request = (HttpRequest) httpObject;
} else if (httpObject instanceof HttpContent) {
doHttpContent(ctx, ((HttpContent) httpObject));
return;
}
if (!request.decoderResult().isSuccess()) {
sendError(ctx, BAD_REQUEST);
return;
}
String uri = request.uri();
if (uri == null) {
sendError(ctx, BAD_REQUEST);
return;
}
HttpMethod method = request.method();
if (HttpMethod.GET.equals(method)) {
File file = new File(BASE_PATH + uri);
if (file.exists()) {
if (file.isFile()) {
RandomAccessFile accessFile = new RandomAccessFile(file, "r");
long length = accessFile.length();
DefaultHttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, OK);
HttpUtil.setContentLength(request, length);
setContentTypeHeader(response, file);
if (HttpUtil.isKeepAlive(request)) {
response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
}
ctx.write(response);
ChannelFuture future = ctx.write(new ChunkedFile(accessFile, 0, length, 2 << 16), ctx.newProgressivePromise());
// future.addListener(new ChannelProgressiveFutureListener() {
// @Override
// public void operationProgressed(ChannelProgressiveFuture future, long progress, long total) throws Exception {
// if (total < 0) {
// //TODO
// } else {
// //TODO
// }
// }
//
// @Override
// public void operationComplete(ChannelProgressiveFuture future) throws Exception {
//
// }
// });
ChannelFuture lastFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
lastFuture.addListener(ChannelFutureListener.CLOSE);
} else {
sendError(ctx, FORBIDDEN);
return;
}
} else {
sendError(ctx, NOT_FOUND);
return;
}
} else if (HttpMethod.POST.equals(method)) {
try {
decoder = new HttpPostRequestDecoder(factory, request);
} catch (Exception e) {
writeResponse(ResponseModel.buildOtherError());
ctx.channel().close();
}
}
}
private void doHttpContent(ChannelHandlerContext ctx,
HttpContent httpContent) throws URISyntaxException {
URI uri = new URI(request.uri());
if (uri.getPath().startsWith("/upload")) {
if (decoder != null) {
try {
decoder.offer(httpContent);
} catch (Exception e) {
e.printStackTrace();
return;
}
if (httpContent instanceof LastHttpContent) {
readHttpDataChunkByChunk();
reset();
}
}
} else {
writeResponse(ResponseModel.buildBadRequestError());
}
}
private void reset() {
if (decoder != null) {
request = null;
decoder.destroy();
decoder = null;
}
}
private void readHttpDataChunkByChunk() {
while (decoder.hasNext()) {
InterfaceHttpData data = decoder.next();
try {
writeHttpData(data);
} catch (Exception e) {
e.printStackTrace();
} finally {
data.release();
}
}
}
private void writeHttpData(InterfaceHttpData data) throws IOException {
if (data.getHttpDataType().equals(InterfaceHttpData.HttpDataType.FileUpload)) {
if (!KEY.equals(data.getName())) {
writeResponse(ResponseModel.buildNoPermissionError());
return;
}
FileUpload fileUpload = (FileUpload) data;
if (fileUpload.isCompleted()) {
String filename = fileUpload.getFilename();
try {
File file = new File(BASE_PATH + filename);
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
if (!file.exists()) {
file.createNewFile();
} else {
writeResponse(ResponseModel.buildOk(filename));
return;
}
fileUpload.renameTo(file);
decoder.removeHttpDataFromClean(fileUpload);
writeResponse(ResponseModel.buildOk(filename));
} catch (Exception e) {
writeResponse(ResponseModel.buildOtherError());
}
} else {
writeResponse(ResponseModel.buildNoPermissionError());
}
}
}
private void writeResponse(@NotNull ResponseModel<String> responseModel) {
String resp = GsonUtil.getGson().toJson(responseModel);
ByteBuf buf = Unpooled.copiedBuffer(resp, CharsetUtil.UTF_8);
FullHttpResponse response = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1, HttpResponseStatus.OK, buf);
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/json; charset=UTF-8");
ctx.write(response);
ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT).addListener(ChannelFutureListener.CLOSE);
}
private void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) {
FullHttpResponse response = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1, status, copiedBuffer("Failure: " + status + "\r\n", CharsetUtil.UTF_8));
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8");
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
private static void setContentTypeHeader(HttpResponse response, File file) {
MimetypesFileTypeMap mimeTypesMap = new MimetypesFileTypeMap();
response.headers().set(HttpHeaderNames.CONTENT_TYPE, mimeTypesMap.getContentType(file.getPath()));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.channel().close();
}
}