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();
}
}