Created
October 4, 2012 05:54
-
-
Save dsdstudio/3831695 to your computer and use it in GitHub Desktop.
Netty 대용량파일 전송
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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