Skip to content

Instantly share code, notes, and snippets.

@dsdstudio
Created October 4, 2012 05:54
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dsdstudio/3831695 to your computer and use it in GitHub Desktop.
Save dsdstudio/3831695 to your computer and use it in GitHub Desktop.
Netty 대용량파일 전송
package net.dsdstudio.sharebox.client;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.RandomAccessFile;
import java.text.NumberFormat;
/**
* Created with IntelliJ IDEA.
* User: bhkim
* Date: 12. 9. 23.
* Time: 오전 3:53
*/
public class SocketClientHandler extends SimpleChannelUpstreamHandler {
Logger logger = LoggerFactory.getLogger(getClass());
private String filepath;
public SocketClientHandler(String filepath) {
this.filepath = filepath;
}
/**
* Protocol 초기화
* [4byte|?byte|8byte|chunk ?byte]
* [filenamelen|filenamedata|filedatalen|filedatadatachunks]
*
* @param file
* @return
*/
private ChannelBuffer initializeProtocol(File file) {
logger.debug("Generate Protocol Buffer");
ChannelBuffer buffer = ChannelBuffers.buffer(file.getName().length() + 12);
buffer.writeInt(file.getName().length());
buffer.writeBytes(file.getName().getBytes());
buffer.writeLong(file.length());
logger.debug("buffer index : {}", buffer.writerIndex());
return buffer;
}
@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
logger.debug("channel connected " + ctx.getChannel().getRemoteAddress());
final Channel channel = ctx.getChannel();
final File file = new File(filepath);
logger.debug("local address : {}", channel.getLocalAddress());
// header write
ChannelFuture future = channel.write(this.initializeProtocol(file));
future.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
logger.debug("header transfer complete. transfer data");
// RandomAccess file을 만든다. FileRegion을 만들기 위함임
RandomAccessFile raf = new RandomAccessFile(file, "r");
// FileRegion을 이용하여 전체를 바로 전송하지않고 내부적으로 chunk 단위로 전송한다.
// 대용량일때는 64k정도로 유지하는것으로 추정됨. 트래픽에따라 chunk 단위가 유동적으로 변함
final FileRegion region = new DefaultFileRegion(raf.getChannel(), 0, file.length(), true);
final ChannelFuture channelFuture = channel.write(region);
logger.debug("file size : {}", region.getCount());
channelFuture.addListener(new ChannelFutureProgressListener() {
@Override
public void operationProgressed(ChannelFuture future, long amount, long current, long total) throws Exception {
logger.debug(String.format("%s : %d / %d (+%d)%n", file.getAbsolutePath(), current, total, amount));
}
@Override
public void operationComplete(ChannelFuture future) throws Exception {
logger.debug("file transfer completed ! [{}:{} bytes]", file.getName(), NumberFormat.getInstance().format(file.length()));
// File 자원 해제
region.releaseExternalResources();
}
});
}
});
}
public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
logger.debug("channel disconnected");
ctx.sendUpstream(e);
}
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
logger.error("", e.getCause());
ctx.getChannel().close();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment