Activemq传输文件
我用的是MQ消息中间件activemq来传输文件的,当然还有很多其他的方式,选择哪种方式还请自行搜索各个消息中间件的特点来定。
ActiveMQ传输文件的方式有byteMessage、StreamMessage、BlobMessage。其中bytemessage和streammessage如果不加工处理的话,只能传输小文件,小于100M的文件应该都可以传,blobmessage可以传输较大的文件,可以用activemq自带的fileserver来做文件的服务器,但是自activemq5.14之后,因为fileserver的漏洞官方就取消了这一模块,所以想用blobmessage的话,还要自己搭建文件服务器如ftp。我这里主要跟大家讲一下byteMessage的传输文件,其实这就几种传输文件的方式,从代码的角度来说都是很相似的。
先说一下我下面这段代码的主要功能吧,功能的主要流程是监听windows下的某一目录,如果有文件(包括新文件和旧文件)就将其移动到另一目录下。
Main方法:初始化消息生产者,初始化消息消费者创建消费者线程监听消息队列,有文件的话创建消息生产者线程将文件以字节的形式发送给消息队列。
代码如下:
publicclassDirTest {
public static void main(String[] args)throwsJMSException {
ExecutorService cachedThreadPool= Executors.newCachedThreadPool();
//初始化主类对象
DirTest main=newDirTest();
//初始化生产者
Producer producter = newProducer();
producter.init();
//初始化消费者对象
Consumer comsumer = newConsumer();
comsumer.init();
//从缓存线程池取出线程,启动消费线程,进行移动文件
cachedThreadPool.execute(newRunnable() {
@Override
public void run() {
try {
comsumer.getMessage("sweep");
} catch(JMSException e){
e.printStackTrace();
}
}
});
// new Thread(main.newConsumerMq(comsumer)).start();
//如果目录下有文件先移出来
String path="D:\\listener";
File file=newFile(path);
for (File temp:file.listFiles()){
if (temp.isFile()){
//创建生产者线程
cachedThreadPool.execute(newRunnable() {
@Override
public void run() {
producter.sendMessage("sweep",temp.getName());
}
});
// new Thread(main.newProductorMq(producter,temp.getName())).start();
}
}
//监听目录
try (WatchService ws = FileSystems.getDefault().newWatchService()){
Path dirToWatch = Paths.get(path);
dirToWatch.register(ws,ENTRY_CREATE,ENTRY_MODIFY,ENTRY_DELETE);
while (true) {
WatchKey key = ws.take();
for (WatchEvent<?> event :key.pollEvents()) {
Kind<?>eventKind = event.kind();
if (eventKind == OVERFLOW) {
System.out.println("Event overflow occurred");
continue;
}
WatchEvent<Path> currEvent = (WatchEvent<Path>) event;
Path dirEntry = currEvent.context();
if ("CREATE".equals(eventKind.toString().substring(6,12))){
// System.out.println("目录新成员:"+dirEntry.toString());
//创建线程将新件名发送至消息队列
cachedThreadPool.execute(newRunnable() {
@Override
public void run() {
producter.sendMessage("sweep",dirEntry.toString());
}
});
}
}
boolean isKeyValid = key.reset();
if (!isKeyValid) {
System.out.println("No longer watching "+ dirToWatch);
break;
}
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
消息生产者:主要负责发送文件字节流给消息队列,并加入回执地址参数,并接收回执消息。代码如下:
/** * 消息队列消息生产者 */ public class Producer { //ActiveMq 的默认用户名 private static final String USERNAME = ActiveMQConnection.DEFAULT_USER; //ActiveMq 的默认登录密码 private static final String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD; //ActiveMQ 的链接地址 private static final String BROKEN_URL = "tcp://ip地址:61616"; //链接工厂 ConnectionFactory connectionFactory; //链接对象 ActiveMQConnection connection; //事务管理 ActiveMQSession session; ThreadLocal<MessageProducer> threadLocal = new ThreadLocal<>(); ExecutorService threadPool = Executors.newFixedThreadPool(8); /** * 初始化消息生产者 * @throws JMSException */ public void init() throws JMSException { try { //创建一个链接工厂 ConnectionFactory connectionFactory = new ActiveMQConnectionFactory( USERNAME,PASSWORD,BROKEN_URL ); //从工厂中创建一个链接 connection = (ActiveMQConnection)connectionFactory.createConnection(); //开启链接 connection.start(); //创建一个事务(这里通过参数可以设置事务的级别) session=(ActiveMQSession) connection.createSession( false, Session.AUTO_ACKNOWLEDGE); } catch (JMSException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } /** * 将消息发送至消息队列 * @param disname * @param text */ public void sendMessage(String disname,String text) { InputStream is=null; try { //创建一个点对点消息队列,队列名 Destination queue = session.createQueue(disname); //消息生产者 MessageProducer messageProducer = null; //设置消息生产者 if(threadLocal.get()!=null){ messageProducer = threadLocal.get(); }else{ messageProducer = session.createProducer(queue); threadLocal.set(messageProducer); } File file=new File("D:\\listener\\"+text); while (!file.renameTo(file)){ //当该文件正在被操作时 Thread.sleep(1000); } MessageProducer producer = session.createProducer(queue); BytesMessage bytesMessage=session.createBytesMessage(); is = new FileInputStream(file); // 读取数据到byte数组中 byte[] buffer=new byte[is.available()]; is.read(buffer); bytesMessage.writeBytes(buffer); //创建一个回执地址 Destination reback=session.createQueue("reback"); MessageConsumer reConsumer=session.createConsumer(reback); reConsumer.setMessageListener(new reListener(session)); //将回执地址写到消息 bytesMessage.setJMSReplyTo(reback); bytesMessage.setStringProperty("FileName",text); producer.send(bytesMessage); is.close(); //删除文件 if (file.exists()&&file.isFile()){ file.delete(); } System.out.println(file.getName()+"发送消息成功!"); } catch (JMSException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { } } private class reListener implements MessageListener { ActiveMQSession session=null; public reListener(ActiveMQSession session) { this.session=session; } @Override public void onMessage(Message message) { threadPool.execute(new Runnable() { @Override public void run() { TextMessage tx= (TextMessage) message; try { System.out.println(tx.getText()+"----"); } catch (JMSException e) { e.printStackTrace(); } } }); } } }
消息消费者:接收消息队列中的文件流信息,并写入本地,将处理后的消息回执给生产者。代码如下:
/** * 消息队列消费者 */ public class Consumer { private static final String USERNAME = ActiveMQConnection.DEFAULT_USER; private static final String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD; private static final String BROKEN_URL = "tcp://10.66.10.36:61616"; ConnectionFactory connectionFactory; Connection connection; Session session; ThreadLocal<MessageConsumer> threadLocal = new ThreadLocal<>(); /** * 初始化消息队列消费者连接池、事务 */ public void init(){ try { connectionFactory = new ActiveMQConnectionFactory(USERNAME,PASSWORD,"tcp://10.66.10.36:61616"); connection = connectionFactory.createConnection(); connection.start(); //创建事务 session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE); } catch (JMSException e) { e.printStackTrace(); } } /** * 建立点对点消费者消息队列,通过MessageListener监听消息队列 * @param disname * @throws JMSException */ public void getMessage(String disname) throws JMSException { //创建点对点消息队列 Destination queue = session.createQueue(disname); MessageConsumer consumer = null; //设置消息队列消费方式(这里是消息监听器) if (threadLocal.get() != null) { consumer = threadLocal.get(); } else { consumer = session.createConsumer(queue); consumer.setMessageListener(new Listener(session)); threadLocal.set(consumer); } } }
Listener监听类:
/** * 消息队列监听器 */ public class Listener implements MessageListener { private Session session; public Listener(Session session){ this.session=session; } //创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。 private ExecutorService threadPool = Executors.newFixedThreadPool(8); private long startTime=System.currentTimeMillis(); @Override public void onMessage(Message message) { threadPool.execute(new Runnable() { @Override public void run() { String fileName=""; FileOutputStream out=null; try { if (message instanceof BytesMessage) { BytesMessage bytesMessage= (BytesMessage) message; fileName=bytesMessage.getStringProperty("FileName"); out=new FileOutputStream("D:\\sink\\"+fileName); byte[] bytes=new byte[1024]; int len=0; while ((len=bytesMessage.readBytes(bytes))!=-1){ out.write(bytes,0,len); } //获得回执地址 Destination recall_destination = message.getJMSReplyTo(); // 创建回执消息 TextMessage textMessage = session.createTextMessage(fileName+"已处理完毕"); // 以上收到消息之后,从新创建生产者,然后在回执过去 MessageProducer producer = session.createProducer(recall_destination); producer.send(textMessage); } } catch (JMSException e) { //递归 if (System.currentTimeMillis()-startTime<=15000){ onMessage(message); } else{ System.out.println(fileName+"文件传输失败,有可能是文件过大!"); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { System.out.println(fileName+"传输失败!"); }finally { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } }); } }