http://blog.csdn.net/huaye2007/article/details/7737487
网上查了堆资料,传送文件名都是写死了的。那如何同时传送文件和文件名?这就要用到Mina 的编码和解码,在刚接触mina的时候,大家可以先看下这个pdf Apache_Mina_Server_2.0中文参考手册V1.0.pdf 这个文档刚开始一般大家都会很容易传送字符串,但是到传送文件就有点难了。
直接介绍编码和解码了:
- package com.blazefire.client;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileNotFoundException;
- import org.apache.mina.core.service.IoHandlerAdapter;
- import org.apache.mina.core.session.IoSession;
- import com.blazefire.bean.BaseMessage;
- import com.blazefire.bean.FileBean;
- import com.blazefire.util.BeanUtil;
- import com.blazefire.util.FileHelper;
- public class ClientHandler extends IoHandlerAdapter{
- /**
- * 客户端接收到信息
- * */
- public void messageReceived(IoSession session, Object message)
- throws Exception {
- // TODO Auto-generated method stub
- super.messageReceived(session, message);
- }
- public void sessionOpened(IoSession session) {
- BaseMessage baseMessage = new BaseMessage();
- baseMessage.setDataType(BeanUtil.UPLOAD_FILE);
- FileBean bean = new FileBean();
- File file = new File("e:\\中国.jpg");
- bean.setFileName(file.getName());
- bean.setFileSize((int)file.length());
- try {
- FileHelper helper =new FileHelper();
- bean.setFileContent(helper.getContent(file));
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- baseMessage.setData(bean);
- session.write(baseMessage);
- }
- public void sessionCreated(IoSession session) throws Exception {
- // TODO Auto-generated method stub
- super.sessionCreated(session);
- }
- }
package com.blazefire.client;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
import com.blazefire.bean.BaseMessage;
import com.blazefire.bean.FileBean;
import com.blazefire.util.BeanUtil;
import com.blazefire.util.FileHelper;
public class ClientHandler extends IoHandlerAdapter{
/**
* 客户端接收到信息
* */
public void messageReceived(IoSession session, Object message)
throws Exception {
// TODO Auto-generated method stub
super.messageReceived(session, message);
}
public void sessionOpened(IoSession session) {
BaseMessage baseMessage = new BaseMessage();
baseMessage.setDataType(BeanUtil.UPLOAD_FILE);
FileBean bean = new FileBean();
File file = new File("e:\\中国.jpg");
bean.setFileName(file.getName());
bean.setFileSize((int)file.length());
try {
FileHelper helper =new FileHelper();
bean.setFileContent(helper.getContent(file));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
baseMessage.setData(bean);
session.write(baseMessage);
}
public void sessionCreated(IoSession session) throws Exception {
// TODO Auto-generated method stub
super.sessionCreated(session);
}
}
在连接成功后,调用sessionOpened方法,读取本地文件,向服务器传送文件,其中用到的一些类将在文章末尾贴出。
传送文件需要文件名,文件大小,文件byte数组: 文件名这个不用说,文件大小是为了解码的时候知道读取到哪个字节就可以停止了。文件byte数组 存储文件内容
- BaseMessage baseMessage = new BaseMessage();
- baseMessage.setDataType(BeanUtil.UPLOAD_FILE);//BeanUtil.UPLOAD_FILE值为1
BaseMessage baseMessage = new BaseMessage();
baseMessage.setDataType(BeanUtil.UPLOAD_FILE);//BeanUtil.UPLOAD_FILE值为1
准备好这些信息之后,调用session.write方法开始编码。
- package com.blazefire.util;
- import java.nio.charset.Charset;
- import org.apache.mina.core.buffer.IoBuffer;
- import org.apache.mina.core.session.IoSession;
- import org.apache.mina.filter.codec.ProtocolEncoderOutput;
- import org.apache.mina.filter.codec.demux.MessageEncoder;
- import com.blazefire.bean.BaseMessage;
- import com.blazefire.bean.FileBean;
- public class BaseMessageEncoder implements MessageEncoder<BaseMessage> {
- /**
- * 基本信息编码
- * */
- public void encode(IoSession session, BaseMessage message,ProtocolEncoderOutput outPut) throws Exception {
- // TODO Auto-generated method stub
- IoBuffer buffer = IoBuffer.allocate(1024*1024*50);
- buffer.putInt(message.getDataType());
- FileBean bean = (FileBean) message.getData();
- byte[] byteStr = bean.getFileName().getBytes(BeanUtil.charset);
- buffer.putInt(byteStr.length);
- buffer.putInt(bean.getFileSize());
- buffer.put(byteStr);
- buffer.put(bean.getFileContent());
- buffer.flip();
- outPut.write(buffer);
- System.out.println("编码完成!");
- }
- }
package com.blazefire.util;
import java.nio.charset.Charset;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
import org.apache.mina.filter.codec.demux.MessageEncoder;
import com.blazefire.bean.BaseMessage;
import com.blazefire.bean.FileBean;
public class BaseMessageEncoder implements MessageEncoder<BaseMessage> {
/**
* 基本信息编码
* */
public void encode(IoSession session, BaseMessage message,ProtocolEncoderOutput outPut) throws Exception {
// TODO Auto-generated method stub
IoBuffer buffer = IoBuffer.allocate(1024*1024*50);
buffer.putInt(message.getDataType());
FileBean bean = (FileBean) message.getData();
byte[] byteStr = bean.getFileName().getBytes(BeanUtil.charset);
buffer.putInt(byteStr.length);
buffer.putInt(bean.getFileSize());
buffer.put(byteStr);
buffer.put(bean.getFileContent());
buffer.flip();
outPut.write(buffer);
System.out.println("编码完成!");
}
}
buffer.putInt(message.getDataType());首先放入了一个int值1 放入这个值,主要是因为这个项目要用到多个解码器,针对不同的信息进行不同的编码,在解码的时候会根据这个参数来判断是否要用这个解码器来解码。
byte[] byteStr = bean.getFileName().getBytes(BeanUtil.charset); 将字符串转换成byte数组,BeanUtil.charset Charset charset = Charset.forName("utf-8"); utf-8编码
buffer.putInt(byteStr.length); 记录文件名的byte数组长度
buffer.putInt(bean.getFileSize()); 记录文件的byte数组长度
buffer.put(byteStr); 存入到buffer
buffer.put(bean.getFileContent());存入到buffer
编码就到此完成了。
现在开始服务器的解码:
- package com.blazefire.util;
- import java.io.File;
- import java.io.FileOutputStream;
- import java.nio.charset.Charset;
- import org.apache.mina.core.buffer.IoBuffer;
- import org.apache.mina.core.session.AttributeKey;
- import org.apache.mina.core.session.IoSession;
- import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
- import org.apache.mina.filter.codec.ProtocolDecoderOutput;
- import org.apache.mina.filter.codec.demux.MessageDecoder;
- import org.apache.mina.filter.codec.demux.MessageDecoderResult;
- import com.blazefire.bean.BaseMessage;
- import com.blazefire.bean.FileBean;
- public class BaseMessageDecoder implements MessageDecoder {
- private AttributeKey CONTEXT = new AttributeKey(getClass(), "context");
- /**
- * 是否适合解码
- * */
- public MessageDecoderResult decodable(IoSession session, IoBuffer in) {
- // TODO Auto-generated method stub
- Context context = (Context) session.getAttribute(CONTEXT);
- if(context == null){
- context = new Context();
- context.dataType = in.getInt();
- if(context.dataType == BeanUtil.UPLOAD_FILE){
- context.strLength = in.getInt();
- context.byteStr = new byte[context.strLength];
- context.fileSize = in.getInt();
- context.byteFile = new byte[context.fileSize];
- session.setAttribute(CONTEXT, context);
- return MessageDecoderResult.OK;
- }else{
- return MessageDecoderResult.NOT_OK;
- }
- }else{
- if(context.dataType == BeanUtil.UPLOAD_FILE){
- return MessageDecoderResult.OK;
- }else{
- return MessageDecoderResult.NOT_OK;
- }
- }
- }
- /**
- * 数据解码
- * */
- public MessageDecoderResult decode(IoSession session, IoBuffer in,
- ProtocolDecoderOutput outPut) throws Exception {
- // TODO Auto-generated method stub
- System.out.println("开始解码:");
- Context context = (Context) session.getAttribute(CONTEXT);
- if(!context.init){
- context.init = true;
- in.getInt();
- in.getInt();
- in.getInt();
- }
- byte[] byteFile = context.byteFile;
- int count = context.count;
- while(in.hasRemaining()){
- byte b = in.get();
- if(!context.isReadName){
- context.byteStr[count] = b;
- if(count == context.strLength-1){
- context.fileName = new String(context.byteStr,BeanUtil.charset);
- System.out.println(context.fileName);
- count = -1;
- context.isReadName = true;
- }
- }
- if(context.isReadName && count != -1){
- byteFile[count] = b;
- }
- // byteFile[count] = b;
- count++;
- }
- context.count = count;
- System.out.println("count:"+count);
- System.out.println("context.fileSize:"+context.fileSize);
- session.setAttribute(CONTEXT, context);
- if(context.count == context.fileSize){
- BaseMessage message = new BaseMessage();
- message.setDataType(context.dataType);
- FileBean bean = new FileBean();
- bean.setFileName(context.fileName);
- bean.setFileSize(context.fileSize);
- bean.setFileContent(context.byteFile);
- message.setData(bean);
- outPut.write(message);
- context.reset();
- }
- return MessageDecoderResult.OK;
- }
- /**
- *
- * */
- public void finishDecode(IoSession session, ProtocolDecoderOutput outPut)
- throws Exception {
- // TODO Auto-generated method stub
- System.out.println("end:::::::::::::::::");
- }
- private class Context{
- public int dataType;
- public byte[] byteFile;
- public int count;
- public int strLength;
- public boolean isReadName;
- public int fileSize;
- public byte[] byteStr;
- public String fileName;
- public boolean init = false;
- public void reset(){
- dataType = 0;
- byteFile = null;
- count = 0;
- strLength = 0;
- isReadName = false;
- fileSize = 0;
- byteStr = null;
- fileName = null;
- }
- }
- }
package com.blazefire.util;
import java.io.File;
import java.io.FileOutputStream;
import java.nio.charset.Charset;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.AttributeKey;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.apache.mina.filter.codec.demux.MessageDecoder;
import org.apache.mina.filter.codec.demux.MessageDecoderResult;
import com.blazefire.bean.BaseMessage;
import com.blazefire.bean.FileBean;
public class BaseMessageDecoder implements MessageDecoder {
private AttributeKey CONTEXT = new AttributeKey(getClass(), "context");
/**
* 是否适合解码
* */
public MessageDecoderResult decodable(IoSession session, IoBuffer in) {
// TODO Auto-generated method stub
Context context = (Context) session.getAttribute(CONTEXT);
if(context == null){
context = new Context();
context.dataType = in.getInt();
if(context.dataType == BeanUtil.UPLOAD_FILE){
context.strLength = in.getInt();
context.byteStr = new byte[context.strLength];
context.fileSize = in.getInt();
context.byteFile = new byte[context.fileSize];
session.setAttribute(CONTEXT, context);
return MessageDecoderResult.OK;
}else{
return MessageDecoderResult.NOT_OK;
}
}else{
if(context.dataType == BeanUtil.UPLOAD_FILE){
return MessageDecoderResult.OK;
}else{
return MessageDecoderResult.NOT_OK;
}
}
}
/**
* 数据解码
* */
public MessageDecoderResult decode(IoSession session, IoBuffer in,
ProtocolDecoderOutput outPut) throws Exception {
// TODO Auto-generated method stub
System.out.println("开始解码:");
Context context = (Context) session.getAttribute(CONTEXT);
if(!context.init){
context.init = true;
in.getInt();
in.getInt();
in.getInt();
}
byte[] byteFile = context.byteFile;
int count = context.count;
while(in.hasRemaining()){
byte b = in.get();
if(!context.isReadName){
context.byteStr[count] = b;
if(count == context.strLength-1){
context.fileName = new String(context.byteStr,BeanUtil.charset);
System.out.println(context.fileName);
count = -1;
context.isReadName = true;
}
}
if(context.isReadName && count != -1){
byteFile[count] = b;
}
// byteFile[count] = b;
count++;
}
context.count = count;
System.out.println("count:"+count);
System.out.println("context.fileSize:"+context.fileSize);
session.setAttribute(CONTEXT, context);
if(context.count == context.fileSize){
BaseMessage message = new BaseMessage();
message.setDataType(context.dataType);
FileBean bean = new FileBean();
bean.setFileName(context.fileName);
bean.setFileSize(context.fileSize);
bean.setFileContent(context.byteFile);
message.setData(bean);
outPut.write(message);
context.reset();
}
return MessageDecoderResult.OK;
}
/**
*
* */
public void finishDecode(IoSession session, ProtocolDecoderOutput outPut)
throws Exception {
// TODO Auto-generated method stub
System.out.println("end:::::::::::::::::");
}
private class Context{
public int dataType;
public byte[] byteFile;
public int count;
public int strLength;
public boolean isReadName;
public int fileSize;
public byte[] byteStr;
public String fileName;
public boolean init = false;
public void reset(){
dataType = 0;
byteFile = null;
count = 0;
strLength = 0;
isReadName = false;
fileSize = 0;
byteStr = null;
fileName = null;
}
}
}
decodable方法就是判断是否要用当前这个解码器
如果你看过上面提到的pdf中 对于这个 CumulativeProtocolDecoder解码器的一些介绍和使用,特别是复杂的解码器的说明这块
你就会知道为什么这里会用到 CONTEXT 这个属性,因为编码的时候,文件内容较长,分成了许多数据包,这个时候在解码的时候就需要记录解码到那个字节,下次再解码的时候就从这个字节开始。
这里需要说下注意点 当前解码器实现的是MessageDecoder这个接口。而pdf中解码器是继承 CumulativeProtocolDecoder这个类。
CumulativeProtocolDecoder这个类 在doDecode方法中需要自己去判断是否需要继续调用doDecode这个方法来完成解码。如果还需要数据解码,这返回值为true,如果解码已完成则直接返回false.
而MessageDecoder这个接口不需要自己去判断
这个类会自己调用自己来将数据传送完,所以每次都会调用这个类里面的方法,每次都会将成员变量初始化.
解码这块主要是解决多个数据包的解码拼成一条完成的消息,这个问题解决了,就没有啥难题了。
- if(!context.init){
- context.init = true;
- in.getInt();
- in.getInt();
- in.getInt();
- }
if(!context.init){
context.init = true;
in.getInt();
in.getInt();
in.getInt();
}
加这个是将那些传入的整数字节读取掉,只剩下文件名的字节和文件字节。
读取完之后,记得重置context的值,否则将影响后续文件的读取。
处理完这些之后,就是直接写文件了。
- package com.blazefire.server;
- import java.io.FileOutputStream;
- import org.apache.mina.core.service.IoHandlerAdapter;
- import org.apache.mina.core.session.IoSession;
- import com.blazefire.bean.BaseMessage;
- import com.blazefire.bean.FileBean;
- public class ServerHandler extends IoHandlerAdapter{
- public void sessionCreated(IoSession session) throws Exception {
- // TODO Auto-generated method stub
- super.sessionCreated(session);
- }
- public void sessionOpened(IoSession session) throws Exception {
- // TODO Auto-generated method stub
- super.sessionOpened(session);
- }
- /**
- * 服务器接收到消息
- * */
- public void messageReceived(IoSession session, Object message)
- throws Exception {
- // TODO Auto-generated method stub
- super.messageReceived(session, message);
- System.out.println("==============");
- BaseMessage baseMessage = (BaseMessage) message;
- FileBean bean = (FileBean) baseMessage.getData();
- System.out.println(bean.getFileName());
- FileOutputStream os = new FileOutputStream("f:\\"+bean.getFileName());
- os.write(bean.getFileContent());
- os.close();
- }
- public void exceptionCaught(IoSession session, Throwable cause)
- throws Exception {
- // TODO Auto-generated method stub
- super.exceptionCaught(session, cause);
- }
- }
package com.blazefire.server;
import java.io.FileOutputStream;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
import com.blazefire.bean.BaseMessage;
import com.blazefire.bean.FileBean;
public class ServerHandler extends IoHandlerAdapter{
public void sessionCreated(IoSession session) throws Exception {
// TODO Auto-generated method stub
super.sessionCreated(session);
}
public void sessionOpened(IoSession session) throws Exception {
// TODO Auto-generated method stub
super.sessionOpened(session);
}
/**
* 服务器接收到消息
* */
public void messageReceived(IoSession session, Object message)
throws Exception {
// TODO Auto-generated method stub
super.messageReceived(session, message);
System.out.println("==============");
BaseMessage baseMessage = (BaseMessage) message;
FileBean bean = (FileBean) baseMessage.getData();
System.out.println(bean.getFileName());
FileOutputStream os = new FileOutputStream("f:\\"+bean.getFileName());
os.write(bean.getFileContent());
os.close();
}
public void exceptionCaught(IoSession session, Throwable cause)
throws Exception {
// TODO Auto-generated method stub
super.exceptionCaught(session, cause);
}
}
mina这块刚开始对编码解码也不是很熟悉,后来还是根据别人的代码,自己测试才最终出了这个版本。本只想让大家自己自己动手做出来,这样也可以更深入了解。为了方便大家,还是贴下源码算了。