[size=large][b]Server端:[/b][/size]
1. 定义远程通讯的接口和与接口相关的类(这里只有RemoteFileInfo.java)。该接口必须继承接口Remote,它里面的方法都要抛出RemoteException异常。
[size=medium]FileManager.java[/size]
import java.rmi.Remote;import java.rmi.RemoteException;public interface FileManager extends Remote { /** * 从远程服务器获取文件信息 * * @param path * @param fileName * @return * @throws RemoteException */ public RemoteFileInfo getRemoteFile(String path, String fileName) throws RemoteException;}
[size=medium]RemoteFileInfo.java[/size]
import java.io.Serializable;import java.util.List;/** * 文件实体类 */public class RemoteFileInfo implements Serializable{ private String fileName; // 文件名// private byte[] content; private List content; // 文件内容 (每1024个字节的内容作为列表的一个元素) public RemoteFileInfo(String fileName, List content) { this.fileName = fileName; this.content = content; } public List getContent() { return content; } public void setContent(List content) { this.content = content; } public String getFileName() { return fileName; } public void setFileName(String fileName) { this.fileName = fileName; }}
2. 实现接口,根据RMI规范,接口实现类的名应以“接口文件名+Impl.java”命名。此类不仅要实现远程接口,还要继承UnicastRemoteObject类,声明构造函数(构造函数也要抛出RemoteException异常)
[size=medium]FileManagerImpl.java[/size]
import java.io.BufferedInputStream;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.rmi.RemoteException;import java.rmi.server.UnicastRemoteObject;import java.util.ArrayList;import java.util.List;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;/** * 文件操作实现类 */public class FileManagerImpl extends UnicastRemoteObject implements FileManager {private static final Log log = LogFactory.getLog(FileManagerImpl.class); public FileManagerImpl() throws RemoteException { super(); } public RemoteFileInfo getRemoteFile(String path, String fileName) throws RemoteException { try { RemoteFileInfo remoteFile; List content = new ArrayList(); File file = new File(path + fileName); BufferedInputStream in = new BufferedInputStream( new FileInputStream(file)); // 处理大文件时,每次读取1024个字节存到centent对象 byte[] bufContent = new byte[1024]; while(in.read(bufContent) !=-1){ content.add(bufContent); bufContent = new byte[1024]; } /* // 只能用到内容少的文件,遇到大文件会出现内容溢出错误 byte[] content = new byte[(int)file.length()]; in.read(content); */ remoteFile = new RemoteFileInfo(fileName,content); log.info("Getting ["+fileName+"] is successful"); return remoteFile; } catch (IOException e) { log.error("Failed to get ["+fileName+"] ..."); e.printStackTrace(); return null; } }
3. 生成Stub类(JDK为5.0或以上可以忽略此步),作此步时要先生成了实现类的class文件.
使用javac命令生成.class文件。(e.g. Unix下:javac -classpath .:./lib/commons-logging.jar:
./lib/log4j-1.2.15.jar package1/package2/FileManagerImpl.java)
在命令行中运行 rmic –v1.2 FileManagerImpl,若实现类被打包要加上包名(e.g. rmic –v1.2 packageA.packageB.packageC. FileManagerImpl).
若执行成功则会生成一个FileManagerImpl_Stub.class的类文件.
4. 实现RMI应用布署程序
[size=medium]FileServer.java[/size]
import java.io.InputStream;import java.io.IOException;import java.net.MalformedURLException;import java.rmi.Naming;import java.rmi.RemoteException;import java.rmi.registry.LocateRegistry;import java.util.HashMap;import java.util.Map;import java.util.Properties;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;/** * 布署应用*/public class FileServer { private static final Log log = LogFactory.getLog(FileServer.class); public static final String CONFIG_FILE_PATH = "/conf/config.properties"; /** * 获取配置信息 * @return URL */ public static Map getURL(){ try{ String url, port; Map configs = new HashMap(); Properties prop = new Properties(); InputStream in = FileServer.class.getResourceAsStream(CONFIG_FILE_PATH); prop.load(in); url = prop.getProperty("server.interface"); port = prop.getProperty("server.port"); configs.put("url", url); configs.put("port", port); return configs; } catch (IOException e){ System.out.println("读取配置信息失败!"); e.printStackTrace(); return null; } } public static void main(String[] args){ try { Map configs = getURL(); if(configs != null){ String port = configs.get("port").toString(); String url = configs.get("url").toString(); // 根据端口号开启一个RMI服务.也可以不指定,默认为1099 LocateRegistry.createRegistry(Integer.parseInt(port)); FileManagerImpl fileMng = new FileManagerImpl(); log.info("Binding URL:"+url); // 注册一个RMI应用 // e.g. url: //127.0.0.1:20002/FILE_MNG Naming.rebind(url, fileMng); log.info("Deploy Remote File Server successfully!"); } else{ log.error("Binding Remote File Server failed..."); } } catch (RemoteException e) { e.printStackTrace(); } catch (MalformedURLException e) { e.printStackTrace(); } }}
5. 为了便于维护,最好将以上程序打到一个JAR包中,在META-INF下的MANIFEST.MF文件中指定运行JAR包需要的Main方法所在的类等信息,像这样:
Manifest-Version: 1.0Main-Class: package1.package2..FileServerClass-Path: webapp-server.jar commons-logging.jar log4j-1.2.15.jar在Unix中用nohup命令在后台启动这个JAR包:
nohup java -jar lib/webapp-server.jar >> $HOME/nohupout/fileserver_log.out &
用ps –ef|grep webapp查看此进程。
如没有进程启动,查看日志文件的错误信息。
[size=large][b]客户端[/b][/size]1. 客户端需要的文件类有: 远程接口FileManager.java及其相关类,Stub类.
业务类
import java.io.BufferedOutputStream;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.net.MalformedURLException;import java.rmi.Naming;import java.rmi.NotBoundException;import java.rmi.RemoteException;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.Properties;import nantian.fxjk.web.util.AccessRemoteServer;/** * 后台文件操作业务类 */public class FileClient { /** * 获取远程服务器上的文件信息 * * @param path 文件路径 * @param fileName 文件名 * @return */ public RemoteFileInfo getRemoteFile(String path, String fileName){ try{ Map configs = getConfig(); if (configs != null) { String url = configs.get("url").toString(); String defaultPath = configs.get("abs_path").toString(); // 查找一个已布署的远程服务 // e.g. url: //128.128.96.2:20002/FILE_MNG FileManager fileMng = (FileManager) Naming.lookup(url); RemoteFileInfo file = fileMng.getRemoteFile( defaultPath + path, fileName); return file; } else{ System.out.println("读取配置信息失败."); return null; } } catch (MalformedURLException e) { e.printStackTrace(); return null; } catch (RemoteException e) { e.printStackTrace(); return null; } catch (NotBoundException e) { e.printStackTrace(); return null; } } /** * 将远程服务器上的文件取到本地 * * @param fileInfo * @return */ public boolean getRemoteFileToLocal(RemoteFileInfo fileInfo, String downloadPath) { File path = new File(downloadPath); if (!path.exists()) { path.mkdirs(); } try { BufferedOutputStream out = new BufferedOutputStream( new FileOutputStream(downloadPath + fileInfo.getFileName())); List content = fileInfo.getContent(); for(int i=0; i<content.size(); i++){ out.write((byte[])content.get(i)); } out.close(); return true; } catch (IOException e) { e.printStackTrace(); return false; } } /** * 获取访问远程对象的相关配置信息 * * @return 配置信息 */ public Map getConfig() { try { Map configs = new HashMap(); Properties prop = new Properties(); InputStream in = FileClient.class .getResourceAsStream(AccessRemoteServer.CONFIG_FILE_PATH); prop.load(in); configs.put("url", prop.getProperty("server.interface")); configs.put("abs_path", prop.getProperty("server.path")); configs.put("down_path", prop.getProperty("download.path")); return configs; } catch (IOException e) { e.printStackTrace(); return null; } }
2. 编写响应请求的Servlet类
[size=medium]DownloadSysFileAction.java[/size]
import java.io.IOException;import java.io.OutputStream;import java.io.PrintWriter;import java.util.List;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;/** * 下载后台文件的请求响应类*/public class DownloadSysFileAction extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out; OutputStream outStream; FileClient fc = new FileClient(); String fileLocation = request.getParameter("file"); fileLocation = fileLocation.trim(); String path = getPath(fileLocation); String fileName = getFileName(fileLocation); RemoteFileInfo file = fc.getRemoteFile(path, fileName); if(file != null){ List content = file.getContent(); outStream = response.getOutputStream(); response.reset(); response.setContentType("application/x-msdownload"); // 纯下载方式 response.setHeader("content-disposition", "attachment; filename="+file.getFileName()); for(int i=0; i<content.size(); i++){ outStream.write((byte[]) content.get(i)); } response.flushBuffer(); outStream.close(); } else{ response.setContentType("text/html;charset=UTF-8"); out = response.getWriter(); out.println("<script type='text/javascript'>"); out.println("alert('不存在该文件!'); window.close();"); out.println("</script>"); out.close(); return ; } } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request,response); } // … … … }
3. 前台页面提供要下载的文件名和所在路径,再调用这个Servlet就可以下载远程服务器上的文件了