Netty学习(十)-Netty文件上传

        Bootstrap b = new Bootstrap();
        b.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true).handler(new ChannelInitializer<Channel>() {

            @Override
            protected void initChannel(Channel ch) throws Exception {
                ch.pipeline().addLast(new ObjectEncoder());
                ch.pipeline().addLast(new ObjectDecoder(ClassResolvers.weakCachingConcurrentResolver(null)));
                ch.pipeline().addLast(new FileUploadClientHandler(fileUploadFile));
            }
        });
        ChannelFuture f = b.connect(host, port).sync();
        f.channel().closeFuture().sync();
    } finally {
        group.shutdownGracefully();
    }
}

public static void main(String[] args) {
    int port = 8080;
    if (args != null && args.length > 0) {
        try {
            port = Integer.valueOf(args[0]);
        } catch (NumberFormatException e) {
            e.printStackTrace();
        }
    }
    try {
        FileUploadFile uploadFile = new FileUploadFile();
        File file = new File("c:/1.txt");
        String fileMd5 = file.getName();// 文件名
        uploadFile.setFile(file);
        uploadFile.setFile_md5(fileMd5);
        uploadFile.setStarPos(0);// 文件开始位置
        new FileUploadClient().connect(port, "127.0.0.1", uploadFile);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

}


client端handler:



import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;

public class FileUploadClientHandler extends ChannelInboundHandlerAdapter {
private int byteRead;
private volatile int start = 0;
private volatile int lastLength = 0;
public RandomAccessFile randomAccessFile;
private FileUploadFile fileUploadFile;

public FileUploadClientHandler(FileUploadFile ef) {
    if (ef.getFile().exists()) {
        if (!ef.getFile().isFile()) {
            System.out.println("Not a file :" + ef.getFile());
            return;
        }
    }
    this.fileUploadFile = ef;
}

public void channelActive(ChannelHandlerContext ctx) {
    try {
        randomAccessFile = new RandomAccessFile(fileUploadFile.getFile(), "r");
        randomAccessFile.seek(fileUploadFile.getStarPos());
        lastLength = (int) randomAccessFile.length() / 10;
        byte[] bytes = new byte[lastLength];
        if ((byteRead = randomAccessFile.read(bytes)) != -1) {
            fileUploadFile.setEndPos(byteRead);
            fileUploadFile.setBytes(bytes);
            ctx.writeAndFlush(fileUploadFile);
        } else {
            System.out.println("文件已经读完");
        }
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException i) {
        i.printStackTrace();
    }
}

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    if (msg instanceof Integer) {
        start = (Integer) msg;
        if (start != -1) {
            randomAccessFile = new RandomAccessFile(fileUploadFile.getFile(), "r");
            randomAccessFile.seek(start);
            System.out.println("块儿长度:" + (randomAccessFile.length() / 10));
            System.out.println("长度:" + (randomAccessFile.length() - start));
            int a = (int) (randomAccessFile.length() - start);
            int b = (int) (randomAccessFile.length() / 10);
            if (a < b) {
                lastLength = a;
            }
            byte[] bytes = new byte[lastLength];
            System.out.println("-----------------------------" + bytes.length);
            if ((byteRead = randomAccessFile.read(bytes)) != -1 && (randomAccessFile.length() - start) > 0) {
                System.out.println("byte 长度:" + bytes.length);
                fileUploadFile.setEndPos(byteRead);
                fileUploadFile.setBytes(bytes);
                try {
                    ctx.writeAndFlush(fileUploadFile);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                randomAccessFile.close();
                ctx.close();
                System.out.println("文件已经读完--------" + byteRead);
            }
        }
    }
}

// @Override
// public void channelRead(ChannelHandlerContext ctx, Object msg) throws
// Exception {
// System.out.println("Server is speek :"+msg.toString());
// FileRegion filer = (FileRegion) msg;
// String path = "E://Apk//APKMD5.txt";
// File fl = new File(path);
// fl.createNewFile();
// RandomAccessFile rdafile = new RandomAccessFile(path, "rw");
// FileRegion f = new DefaultFileRegion(rdafile.getChannel(), 0,
// rdafile.length());
//
// System.out.println("This is" + ++counter + "times receive server:["
// + msg + "]");
// }

// @Override
// public void channelReadComplete(ChannelHandlerContext ctx) throws
// Exception {
// ctx.flush();
// }

public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
    cause.printStackTrace();
    ctx.close();
}
// @Override
// protected void channelRead0(ChannelHandlerContext ctx, String msg)
// throws Exception {
// String a = msg;
// System.out.println("This is"+
// ++counter+"times receive server:["+msg+"]");
// }

}


我们还自定义了一个对象,用于统计文件上传进度的:



import java.io.File;
import java.io.Serializable;

public class FileUploadFile implements Serializable {

private static final long serialVersionUID = 1L;
private File file;// 文件
private String file_md5;// 文件名
private int starPos;// 开始位置
private byte[] bytes;// 文件字节数组
private int endPos;// 结尾位置

public int getStarPos() {
    return starPos;
}

public void setStarPos(int starPos) {
    this.starPos = starPos;
}

public int getEndPos() {
    return endPos;
}

public void setEndPos(int endPos) {
    this.endPos = endPos;
}

public byte[] getBytes() {
    return bytes;
}

public void setBytes(byte[] bytes) {
    this.bytes = bytes;
}

public File getFile() {
    return file;
}

public void setFile(File file) {
    this.file = file;
}

public String getFile\_md5() {
    return file_md5;
}

public void setFile\_md5(String file_md5) {
    this.file_md5 = file_md5;
}

}


输出为:



块儿长度:894
长度:8052
-----------------------------894
byte 长度:894
块儿长度:894
长度:7158
-----------------------------894
byte 长度:894
块儿长度:894
长度:6264
-----------------------------894
byte 长度:894
块儿长度:894
长度:5370
-----------------------------894
byte 长度:894
块儿长度:894
长度:4476
-----------------------------894
byte 长度:894
块儿长度:894
长度:3582
-----------------------------894
byte 长度:894
块儿长度:894
长度:2688
-----------------------------894
byte 长度:894
块儿长度:894
长度:1794
-----------------------------894
byte 长度:894
块儿长度:894
长度:900
-----------------------------894
byte 长度:894
块儿长度:894
长度:6
-----------------------------6
byte 长度:6
块儿长度:894
长度:0
-----------------------------0
文件已经读完--------0

Process finished with exit code 0

这样就实现了服务器端文件的上传,当然我们也可以使用http的形式。


server端:



import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

public class HttpFileServer implements Runnable {
private int port;

public HttpFileServer(int port) {
    super();
    this.port = port;
}

@Override
public void run() {
    EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    EventLoopGroup workerGroup = new NioEventLoopGroup();
    ServerBootstrap serverBootstrap = new ServerBootstrap();
    serverBootstrap.group(bossGroup, workerGroup);
    serverBootstrap.channel(NioServerSocketChannel.class);
    //serverBootstrap.handler(new LoggingHandler(LogLevel.INFO));
    serverBootstrap.childHandler(new HttpChannelInitlalizer());
    try {
        ChannelFuture f = serverBootstrap.bind(port).sync();
        f.channel().closeFuture().sync();
    } catch (InterruptedException e) {
        e.printStackTrace();
    } finally {
        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();
    }
}

public static void main(String[] args) {
    HttpFileServer b = new HttpFileServer(9003);
    new Thread(b).start();
}

}


Server端initializer:



import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.stream.ChunkedWriteHandler;

public class HttpChannelInitlalizer extends ChannelInitializer {

@Override
protected void initChannel(SocketChannel ch) throws Exception {
    ChannelPipeline pipeline = ch.pipeline();
    pipeline.addLast(new HttpServerCodec());
    pipeline.addLast(new HttpObjectAggregator(65536));
    pipeline.addLast(new ChunkedWriteHandler());
    pipeline.addLast(new HttpChannelHandler());
}

}


server端hadler:



import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;
import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;
import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN;
import static io.netty.handler.codec.http.HttpResponseStatus.INTERNAL_SERVER_ERROR;
import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelProgressiveFuture;
import io.netty.channel.ChannelProgressiveFutureListener;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.DefaultHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpChunkedInput;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpResponse;
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.stream.ChunkedFile;
import io.netty.util.CharsetUtil;
import io.netty.util.internal.SystemPropertyUtil;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.regex.Pattern;

import javax.activation.MimetypesFileTypeMap;

public class HttpChannelHandler extends SimpleChannelInboundHandler {
public static final String HTTP_DATE_FORMAT = “EEE, dd MMM yyyy HH:mm:ss zzz”;
public static final String HTTP_DATE_GMT_TIMEZONE = “GMT”;
public static final int HTTP_CACHE_SECONDS = 60;

@Override
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
    // 监测解码情况
    if (!request.getDecoderResult().isSuccess()) {
        sendError(ctx, BAD_REQUEST);
        return;
    }
    final String uri = request.getUri();
    final String path = sanitizeUri(uri);
    System.out.println("get file:"+path);
    if (path == null) {
        sendError(ctx, FORBIDDEN);
        return;
    }
    //读取要下载的文件
    File file = new File(path);
    if (file.isHidden() || !file.exists()) {
        sendError(ctx, NOT_FOUND);
        return;
    }
    if (!file.isFile()) {
        sendError(ctx, FORBIDDEN);
        return;
    }
    RandomAccessFile raf;
    try {
        raf = new RandomAccessFile(file, "r");
    } catch (FileNotFoundException ignore) {
        sendError(ctx, NOT_FOUND);
        return;
    }
    long fileLength = raf.length();
    HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
    HttpHeaders.setContentLength(response, fileLength);
    setContentTypeHeader(response, file);
    //setDateAndCacheHeaders(response, file);
    if (HttpHeaders.isKeepAlive(request)) {
        response.headers().set("CONNECTION", HttpHeaders.Values.KEEP_ALIVE);
    }

    // Write the initial line and the header.
    ctx.write(response);

    // Write the content.
    ChannelFuture sendFileFuture =
    ctx.write(new HttpChunkedInput(new ChunkedFile(raf, 0, fileLength, 8192)), ctx.newProgressivePromise());
    //sendFuture用于监视发送数据的状态
    sendFileFuture.addListener(new ChannelProgressiveFutureListener() {
        @Override
        public void operationProgressed(ChannelProgressiveFuture future, long progress, long total) {
            if (total < 0) { // total unknown
                System.err.println(future.channel() + " Transfer progress: " + progress);
            } else {
                System.err.println(future.channel() + " Transfer progress: " + progress + " / " + total);
            }
        }

        @Override
        public void operationComplete(ChannelProgressiveFuture future) {
            System.err.println(future.channel() + " Transfer complete.");
        }
    });

    // Write the end marker
    ChannelFuture lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);

    // Decide whether to close the connection or not.
    if (!HttpHeaders.isKeepAlive(request)) {
        // Close the connection when the whole content is written out.
        lastContentFuture.addListener(ChannelFutureListener.CLOSE);
    }
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
    cause.printStackTrace();
    if (ctx.channel().isActive()) {
        sendError(ctx, INTERNAL_SERVER_ERROR);
    }
    ctx.close();
}

private static final Pattern INSECURE_URI = Pattern.compile(".\*[<>&\"].\*");

private static String sanitizeUri(String uri) {
    // Decode the path.
    try {
        uri = URLDecoder.decode(uri, "UTF-8");
    } catch (UnsupportedEncodingException e) {
        throw new Error(e);
    }

    if (!uri.startsWith("/")) {
        return null;
    }

    // Convert file separators.
    uri = uri.replace('/', File.separatorChar);

    // Simplistic dumb security check.
    // You will have to do something serious in the production environment.
    if (uri.contains(File.separator + '.') || uri.contains('.' + File.separator) || uri.startsWith(".") || uri.endsWith(".")
            || INSECURE_URI.matcher(uri).matches()) {
        return null;
    }

    // Convert to absolute path.
    return SystemPropertyUtil.get("user.dir") + File.separator + uri;
}


private static void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) {
    FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, status, Unpooled.copiedBuffer("Failure: " + status + "\r\n", CharsetUtil.UTF_8));
    response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8");

    // Close the connection as soon as the error message is sent.
    ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}

/\*\*

* Sets the content type header for the HTTP Response
*
* @param response
* HTTP response
* @param file
* file to extract content type
*/
private static void setContentTypeHeader(HttpResponse response, File file) {
MimetypesFileTypeMap m = new MimetypesFileTypeMap();
String contentType = m.getContentType(file.getPath());
if (!contentType.equals(“application/octet-stream”)) {
contentType += “; charset=utf-8”;
}
response.headers().set(CONTENT_TYPE, contentType);
}

}


client端:



import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前在阿里

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Linux运维全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上运维知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化的资料的朋友,可以点击这里获取!

大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**

因此收集整理了一份《2024年最新Linux运维全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
[外链图片转存中…(img-tqeCQQ5j-1714160348940)]
[外链图片转存中…(img-nnoaNAPd-1714160348941)]
[外链图片转存中…(img-sRosjwHE-1714160348941)]
[外链图片转存中…(img-XanbhKg3-1714160348941)]
[外链图片转存中…(img-2tvmHE5F-1714160348942)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上运维知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化的资料的朋友,可以点击这里获取!

  • 27
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值