Netty中使用Apache Common FileUpload

/**
* 用Netty来实现上传
*/
public class NettyFileUpload extends FileUpload {

private NettyRequestContext context;

public static final boolean isMultipartContent(HttpRequest request) {
if (HttpMethod.POST != request.getMethod()) {
return false;
}
if (request.getHeaders("Content-Type") == null && request.getHeaders("Content-Type").size() == 0) {
return false;
}
String contentType = request.getHeaders("Content-Type").get(0);
if (contentType == null) {
return false;
}
if (contentType.toLowerCase().startsWith("multipart/")) {
return true;
}
return false;
}

public NettyFileUpload(NettyRequestContext context) {
this.context = context;
}

public NettyFileUpload(FileItemFactory fileItemFactory) {
super(fileItemFactory);
}

public FileItemIterator getItemIterator() throws FileUploadException, IOException {
return super.getItemIterator(context);
}
}


public class NettyRequestContext implements RequestContext {
private String encoding;
private String contentType;
private int contentLength = -1;
/**
* 上传的内容流
*/
private InputStream inputStream;
public NettyRequestContext(String encoding, String contentType,
int contentLength, InputStream inputStream) {
this.encoding = encoding;
this.contentType = contentType;
this.contentLength = contentLength;
this.inputStream = inputStream;
}
@Override
public String getCharacterEncoding() {
return encoding;
}
@Override
public String getContentType() {
return contentType;
}
@Override
public int getContentLength() {
return contentLength;
}
@Override
public InputStream getInputStream() throws IOException {
// 不能直接用request的流,因为有HttpChunk
return inputStream;
}
@Override
public String toString() {
return "ContentLength=" + this.getContentLength() + ", ContentType="
+ this.getContentType();
}

public void closeInputStream() throws IOException {
getInputStream().close();
}
}


public class NettyChunkInputStream extends InputStream {

private BlockingQueue<HttpChunk> chunkQueue = new ArrayBlockingQueue<HttpChunk>(128);

private HttpChunk currentChunk = null;

private volatile boolean closed;

public boolean putChunk(HttpChunk chunk) throws IOException {
if (!closed) {
try {
chunkQueue.put(chunk);
} catch (InterruptedException e) {
throw new IOException(e);
}
return true;
}
throw new IOException(" this inputstream has been closed!");

}

@Override
public int read() throws IOException {
byte resultByte = -1;
try {
if (getChunk().getContent().readable()) {
resultByte = getChunk().getContent().readByte();
} else if (!getChunk().isLast()) {
nextChunk();
if (getChunk().getContent().readable()) {
resultByte = getChunk().getContent().readByte();
} else {
return -1;
}
} else {
return -1;
}
} catch (InterruptedException e) {
throw new IOException(e);
}
// InputStream.read()返回0-255之间的int
return resultByte >= 0 ? resultByte : 256 + resultByte;
}

private HttpChunk getChunk() throws InterruptedException {
if (currentChunk == null) {
currentChunk = chunkQueue.take();
}

return currentChunk;
}

private void nextChunk() throws InterruptedException {
currentChunk = chunkQueue.take();
}

@Override
public int available() throws IOException {
throw new UnsupportedOperationException("unsupport available()");
}

@Override
public void close() throws IOException {
chunkQueue = null;
closed = true;
}

public boolean isClosed() {
return closed;
}

}

应用:




public class NettyUploadHandler extends SimpleChannelUpstreamHandler {
private static ExecutorService EXECUTOR = Executors.newFixedThreadPool(32);
private boolean hasReadChunk;
private NettyChunkInputStream chunkStream = new NettyChunkInputStream();
private NettyRequestContext context;

private volatile Map<String, String> resultMap = null;

@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
if (!hasReadChunk) {
handleHttpRequest(ctx, e);
} else {
handleHttpChunk(e);
}
}

private void handleHttpRequest(ChannelHandlerContext ctx, MessageEvent e) throws IOException {
HttpRequest request = (HttpRequest) e.getMessage();
if (isUploadFile(request)) {
handleUploadRequest(request);
} else {
ctx.sendUpstream(e);
}
}

private void handleUploadRequest(HttpRequest request) throws IOException {
context = new NettyRequestContext("UTF-8", request.getHeader("Content-Type"), -1, chunkStream);
if (request.isChunked()) {
hasReadChunk = true;
} else {
HttpChunk chunk = new DefaultHttpChunk(request.getContent());
chunkStream.putChunk(chunk);
}
startUpload();
}

private void handleHttpChunk(MessageEvent e) throws IOException {

if (isUploadFinished()) {
writeResult(e.getChannel());
return;
}
HttpChunk chunk = (HttpChunk) e.getMessage();
chunkStream.putChunk(chunk);

if (chunk.isLast()) {
for (;;) {
if (isUploadFinished()) {
writeResult(e.getChannel());
return;
}
}
}
}

private boolean isUploadFinished() {
return resultMap != null || chunkStream.isClosed();
}

private boolean isUploadFile(HttpRequest request) {
return request.getUri().equals("/upload/uploadfile") && NettyFileUpload.isMultipartContent(request);
}

private void startUpload() {
EXECUTOR.execute(new UploadTask());
}

private void writeResult(Channel channel) {
String json = JsonUtil.beanToJson(resultMap);
byte[] data = json.getBytes();
ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(data);
HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
response.setContent(buffer);
response.setHeader(HttpHeaders.Names.CONTENT_TYPE, "text/html; charset=UTF-8");
response.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(buffer.readableBytes()));
channel.write(response);
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
e.getCause().printStackTrace();
}

class UploadTask implements Runnable {

public UploadTask() {
super();
}

@Override
public void run() {
long start = System.currentTimeMillis();

try {

NettyFileUpload upload = new NettyFileUpload(context);
FileItemIterator iter = upload.getItemIterator();

while (iter.hasNext()) {
FileItemStream item = iter.next();
//这里处理逻辑

}
resultMap = handler.getResult();
context.closeInputStream();
long end = System.currentTimeMillis();
System.out.println("spend time : " + (end - start));

} catch (Exception e) {
e.printStackTrace();
}
}

}
}


该NettyChunkInputStream必须一个线程来putChunk(...),另一个线程使用getInputStream()来消耗数据。

PS:可以在NettyChunkInputStream中重写InputStream.read(bs,offset,len),避免每次调用read()都进行边界判断,使之效率更高。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值