java文件服务器,java文件服务器

java文件服务器,java文件服务器,Java web文件服务器的简单实现

我们公司在做一个在线考试平台,平台在发布文章、编辑试题时需要将文章生成的html文档以及题中的图片上传到一个专门文件服务器,然后再界面中使用http链接上传的文件。

基于这个考虑,需要实现一个文件上传程序,上传后程序返回文件的http URL地址。

我在网上找了些解决方案,大多不太适合我们的业务场景,加上文件上传程序本身并不复杂,于是就决定自己写一个。

下面谈谈我的思路及实现。

首先,服务器是暴露在外网的,需要考虑安全问题,不能任何人都可以往上面上传,所以要设计登录机制。

登录原理于web应用登录类似,都是带上账号、密码请求服务器,服务器验证通过后改变session中登录状态。

浏览器再往服务器上传数据,上传完数据后,服务器存储文件并生成URL地址并返回。

本例子基于servlet实现,包含web服务端和客户端,涉及到的类:

服务端 Login.java -- 上传权限验证类,负责验证登录请求,设置session登录状态值

服务端 FileUpload.java -- 文件监听及存储类,负责判断登录状态,存储文件并返回URL

客户端 UploadFileToFileServer.java -- 文件上传类,负责登录并上传文件到服务器。

由于java后台非浏览器环境,需要自行实现POST请求及session操作。

代码:

1.文件服务器为一个web程序,监听并接收POST方式传来的数据,包含两个Servlet,一个客户端登录、一个接收并保存文件,文件上传使用cos.jar包。

FileUpload.java Servlet:

 1 package com.chanjet.exam.fileserver;
 3 import java.io.File;
 4 import java.io.IOException;
 5 import java.io.PrintWriter;
 6 import java.text.SimpleDateFormat;
 7 import java.util.Date;
 8 import java.util.Enumeration;
 10 import javax.servlet.ServletException;
 11 import javax.servlet.http.HttpServlet;
 12 import javax.servlet.http.HttpServletRequest;
 13 import javax.servlet.http.HttpServletResponse;
 15 import com.oreilly.servlet.MultipartRequest;
 16 import com.oreilly.servlet.multipart.FileRenamePolicy;
 18 /***
 19 * 文件接收servlet,集成自HttpServlet并实现cos.jar的FileRenamePolicy接口实现文件接收。
 20 * 本类实现了FileRenamePolicy接口的rename方法对文件进行重命名
 21 * @author jsper 2014-12-6
 22 *
 23 */
 24 public class FileUpload extends HttpServlet implements FileRenamePolicy {
 28 private static final long serialVersionUID = 1L;
 30 // 定义限制文件大小:
 31 private int maxSize = 102400 * 1024;// 102400KB以内(100MB)
 33 // 要保存到web目录下的哪个路径下
 34 private String dectory = "uploads";
 36 // 指定文件后缀名范围,多个用“,”隔开
 37 private String fp = "png,gif,jpg,bmp,html,htm,rar,zip,doc,docx,xml,xls,xlsx,txt,tmp";
 39 //本类构造方法
 40 public FileUpload() {
 41 super();
 42 }
 46 //重写HttpServlet的Get方法
 47 public void doGet(HttpServletRequest request, HttpServletResponse response)
 48 throws ServletException, IOException {
 50 doPost(request, response);
 52 }
 55 //重写HttpServlet的Post方法
 56 public void doPost(HttpServletRequest request, HttpServletResponse response)
 57 throws ServletException, IOException {
 60 request.setCharacterEncoding("UTF-8");
 61 response.setCharacterEncoding("UTF-8");
 63 response.setContentType("text/html");
 64 PrintWriter out = response.getWriter();
 67 //验证是否已登录
 68 Boolean islogined = (Boolean)request.getSession().getAttribute("islogined");
 69 if(islogined==null || (islogined!=null islogined!=true)){
 70 out.println("Error,You are not logged!");
 71 return;
 72 }
 75 // 获得web目录的绝对路径
 76 String path = this.getServletContext().getRealPath("/");
 79 //根据日期构建文件存放目录
 80 SimpleDateFormat df = new SimpleDateFormat("yyyy/MM/dd/HH");
 81 String dectory2 = df.format(new Date());
 84 // 要保存文件的绝对路径
 85 String buildPath = path+dectory+"/"+dectory2+"/";
 86 //目标目录不存在的话就自动创建
 87 File f1 = new File(buildPath);
 88 if (!f1.exists()) {
 89 f1.mkdirs();//建立目录
 90 }
 92 FileUpload fu = new FileUpload();
 93 try {
 94 MultipartRequest multi = new MultipartRequest(request, buildPath,
 95 maxSize, "UTF-8", fu);
 96 Enumeration ? enums = multi.getFileNames();
100 while (enums.hasMoreElements()) {
101 String fileName = (String) enums.nextElement();
102 File file = multi.getFile(fileName);
103 if (file != null) {
104 String name = multi.getFilesystemName(fileName);
105 String webroot = request.getScheme() + "://"
106 + request.getServerName() + ":"
107 + request.getServerPort()
108 + request.getContextPath();
109 String fileurl = webroot+"/"+dectory+"/"+dectory2+"/"+name;
111 out.println(fileurl);
112 }
113 }
114 } catch (Exception e) {
115 out.println("Server Exception!");
116 e.printStackTrace();
117 }
119 out.flush();
120 out.close();
121 }
125 @Override
126 public File rename(File file) {
128 int index = file.getName().lastIndexOf("."); //得到文件名中最后一个.的位置
129 String postfix = file.getName().substring(index); //得到文件名后缀
131 //构建新文件名(使用当前服务器时间戳)
132 String newFileName = System.currentTimeMillis() + postfix;
134 // 判断文件类型是否符合限定范围
135 String[] ps = fp.split(",");
136 boolean flag = false;
137 for (String p : ps) {
138 if (postfix.equals(("." + p))) {
139 flag = true;
140 break;
141 }
142 }
143 if (flag) {
144 return new File(file.getParent(), newFileName);
145 } else {
146 return null;
147 }
149 }
151 }

Login.java Servlet:

 1 package com.chanjet.exam.fileserver;
 3 import java.io.IOException;
 4 import java.io.PrintWriter;
 6 import javax.servlet.ServletException;
 7 import javax.servlet.http.HttpServlet;
 8 import javax.servlet.http.HttpServletRequest;
 9 import javax.servlet.http.HttpServletResponse;
12 /***
13 * 客户端登录类
14 * 
15 * 本servlet将验证用户登录,并为用户创建session状态
16 * 
17 * @author chenjye 2014-12-6
19 */
20 public class Login extends HttpServlet {
22 /**
23 * The doGet method of the servlet.
24 */
25 public void doGet(HttpServletRequest request, HttpServletResponse response)
26 throws ServletException, IOException {
28 response.setContentType("text/html");
29 PrintWriter out = response.getWriter();
30 out.println("Error");
31 out.flush();
32 out.close();
35 /**
36 * The doPost method of the servlet.
37 */
38 public void doPost(HttpServletRequest request, HttpServletResponse response)
39 throws ServletException, IOException {
41 response.setContentType("text/html");
42 PrintWriter out = response.getWriter();
44 System.out.println("fileserver:user logining.");
45 //客户端登录
46 String username = request.getParameter("username");
47 String password = request.getParameter("password");
48 if("admin".equals(username) "123456".equals(password)){
49 request.getSession().setAttribute("islogined",true);
50 out.print(true);
51 System.out.println("fileserver:login success.");
52 }else{
53 out.print("the username or password error.");
54 System.out.println("fileserver:login failure.");
57 out.flush();
58 out.close();
61 }

2.客户端实现了post表单提交方法,一个用于登录并获得session,一个用于执行上传。

UploadFileToFileServer.java 代码如下:

 1 package com.chanjet.core.util;
 3 import java.io.BufferedReader;
 4 import java.io.DataOutputStream;
 5 import java.io.InputStreamReader;
 6 import java.io.OutputStream;
 7 import java.net.HttpURLConnection;
 8 import java.net.URL;
 9 import java.util.Map;
 12 /***
 13 * 上传文件到文件服务器类
 14 * 上传步骤:1.使用login方法登录文件服务器,2.使用fileUpload方法传输文件
 15 * 说明:必须先登录,再传输,登录后需要得到服务器分配的session,上传文件时需要将session发给服务器,以便服务器确认身份。
 16 * @author Admin
 17 *
 18 */
 19 public class UploadFileToFileServer {
 22 //登录文件服务器的地址
 23 private String loginUrl = "http://localhost/fileserver/servlet/Login";
 25 //接收文件的服务器地址
 26 private String serverUrl = "http://localhost/fileserver/servlet/FileUpload";
 28 private String[] session = null;
 30 /**
 31 * 模拟浏览器POST提交数据方法
 32 * 将装进HashMap的参数及值组合成URL并POST提交到web,
 33 * @param action
 34 * @param parMap
 35 * @return
 36 * @throws Exception
 37 */
 38 public String login(Map String,String parMap) throws Exception{
 39 String resultSet = null;
 41 try{
 42 HttpURLConnection huc = (HttpURLConnection) new URL(loginUrl).openConnection();
 43 //指定HTTP内容类型及URL格式为form表单格式
 44 //huc.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
 47 // 设置允许output
 48 huc.setDoOutput(true);
 49 // 设置提交方式为post方式
 50 huc.setRequestMethod("POST");
 51 String parameter="";
 52 for(String key:parMap.keySet()){
 53 //组建参数URL并指定URL及参数编码格式
 54 parameter+=key+"="+ java.net.URLEncoder.encode(parMap.get(key),"utf-8")+" ";
 55 }
 56 parameter=parameter.substring(0, parameter.length()-1);
 57 OutputStream os = huc.getOutputStream();
 58 os.write(parameter.getBytes("utf-8"));//指定URL及参数编码格式
 59 os.flush();
 60 os.close();
 61 //执行提交后获取执行结果
 62 BufferedReader br = new BufferedReader(new InputStreamReader(huc .getInputStream()));
 63 huc.connect();
 64 String line=null ;
 65 resultSet = br.readLine();
 66 //循环按行读取文本流
 67 while ((line = br.readLine()) != null) {
 68 resultSet += line;//此处未加上\r\n
 69 }
 70 br.close();
 71 resultSet = resultSet.trim();
 73 //得到本次会话session,以便传文件时服务器确认身份
 74 session = huc.getHeaderField("Set-Cookie").split(";");
 75 System.out.println("sessionId:"+session[0]);
 77 huc.disconnect();
 78 }catch(Exception e){
 79 throw e;
 80 }
 82 return resultSet;
 83 }
 87 /***
 88 * 上传文件到文件服务器,得到返回的文件的网络地址并返回给调用程序
 89 * chenjye 2014-12-6
 90 * 参考:http://314858770.iteye.com/blog/720456
 91 * 
 92 * @param f
 93 * @param url
 94 * @return
 95 * @throws Exception
 96 */
 97 public String fileUpload(byte[] bytes) throws Exception{
 98 String resultSet = null;
103 try{
104 HttpURLConnection huc = (HttpURLConnection) new URL(serverUrl).openConnection();
107 huc.setRequestMethod("POST");// 设置提交方式为post方式
108 huc.setDoInput(true);
109 huc.setDoOutput(true);//设置允许output
110 huc.setUseCaches(false);//POST不能使用缓存
112 //同步会话session
113 if(session!=null session.length 0){
114 huc.setRequestProperty("Cookie", session[0]);
115 }else{
116 return "Session Error";
117 }
119 //设置请求头信息
120 huc.setRequestProperty("Connection", "Keep-Alive");
121 huc.setRequestProperty("Charset", "UTF-8");
123 // 设置边界
124 String boundary = "----------" + System.currentTimeMillis();
125 huc.setRequestProperty("Content-Type", "multipart/form-data; boundary="+ boundary);
128 // 头部:
129 StringBuilder sb = new StringBuilder();
130 sb.append("--"); // ////////必须多两道线
131 sb.append(boundary);
132 sb.append("\r\n");
133 sb.append("Content-Disposition: form-data; name=\"file\"; filename=\"uploaded_file.html\"\r\n");
134 sb.append("Content-Type: application/octet-stream\r\n\r\n");
136 // 获得输出流
137 OutputStream out = new DataOutputStream(huc.getOutputStream());
138 out.write(sb.toString().getBytes("utf-8"));//写入header
139 // 文件数据部分
140 out.write(bytes, 0, bytes.length);//写入文件数据
142 // 结尾部分
143 byte[] foot = ("\r\n--" + boundary + "--\r\n").getBytes("utf-8");// 定义最后数据分隔线
144 out.write(foot);//写入尾信息
146 out.flush();
147 out.close();
150 //执行提交后获取执行结果
151 BufferedReader br = new BufferedReader(new InputStreamReader(huc.getInputStream()));
152 huc.connect();
153 String line=null ;
154 resultSet = br.readLine();
156 //循环按行读取文本流
157 while ((line = br.readLine()) != null) {
158 resultSet += line+"\r\n";//此处未加上\r\n
159 }
160 br.close();
161 resultSet = resultSet.trim();
162 huc.disconnect();
163 }catch(Exception e){
164 throw e;
165 }
168 return resultSet;
171 }
173 }

3.程序调用:

 1 //虚拟模板,并将内容填充进模板
 2 String title = "动态内容静态化测试";
 3 String content ="测试";
 5 StringBuilder sb = new StringBuilder();
 6 sb.append(" !DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \r\n");
 7 sb.append(" html \r\n");
 8 sb.append(" head \r\n");
 9 sb.append(" meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" \r\n");
10 sb.append(" title "+title+" /title \r\n");
11 sb.append(" /head \r\n");
12 sb.append(" body \r\n");
13 sb.append(content+"\r\n");
14 sb.append(" /body \r\n");
15 sb.append(" /html \r\n");
17 //创建文件传输对象,将内容发送给文件服务器并得到返回的url
18 UploadFileToFileServer uftfs = new UploadFileToFileServer();
19 Map String,String parMap = new HashMap String,String ();
20 parMap.put("username", "admin");
21 parMap.put("password", "123456");
22 String loginFlag = uftfs.login(parMap);//登录
23 if("true".equals(loginFlag)){
24 byte[] bytes = sb.toString().getBytes("UTF-8");
25 String url = uftfs.fileUpload(bytes);//上传
26 System.out.println("文件URL:"+url);
30 //直接传二进制文件
31 byte[] buffer = null; 
32 try { 
33 File file = new File("E:\\girl.jpg"); 
34 FileInputStream fis = new FileInputStream(file); 
35 ByteArrayOutputStream bos = new ByteArrayOutputStream(1000); 
36 byte[] b = new byte[1000]; 
37 int n; 
38 while ((n = fis.read(b)) != -1) { 
39 bos.write(b, 0, n); 
40 } 
41 fis.close(); 
42 bos.close(); 
43 buffer = bos.toByteArray(); 
44 } catch (FileNotFoundException e) { 
45 e.printStackTrace(); 
46 } catch (IOException e) { 
47 e.printStackTrace(); 
48 } 
50 String url = uftfs.fileUpload(buffer);//上传
51 System.out.println("文件URL:"+url);

 

后注:

1.以上代码虽然在上传前进行了登录验证防止黑客攻击,但文件的url是暴露的,缺乏防盗链机制,可以通过判断来路Referer避免常见的盗链。

2.以上例子,文件存储路径由文件服务器安排,但在某些需求下,可能需要由client指定,这时,可以在登录时一并将要放置的路径传给文件服务器,文件服务器验证登录成功后将收到的路径存于session,在接收文件时取出并使用。但这样做需要每次传文件时执行一次登录,但方式并不限于这一种,对程序稍作调整可达到更加合理的效果。

PS:该程序已经持续运行了3年了,没死过机,卡过机,选择上传组件时特意对比了一些测试评论,得知cos.jar很稳定,支持超大文件上传,从此看来果然名不虚传。