码迷,mamicode.com
首页 > Web开发 > 详细

jsp文件上传

时间:2015-09-03 23:14:18      阅读:260      评论:0      收藏:0      [点我收藏+]

标签:

  整理了一下jsp的上传下载,由客户端到服务端,包括一些常规的业务

一客户端

先看最基本的情况

1 在表单设置multipart/form-data,通过提交键把数据和附件一次性提交的。服务器更多的是对应这个例子。

 技术分享

但有以下问题

一般的Ajax无法把附件数据发过去

附件往往涉及比较多问题,比如上传需要时间(要有个进度),上传也有可能不成功等,上传需要校验的东西就比较多。

 

2于是就有把上传分开来自己执行的。上传项在浏览文件后就开始上传,其他表单数据在提交表单时再提交。

 技术分享

更详细地说是:上传在表单中有自成一体的界面,浏览后点击上传开始上传(也可以是其他形式),以ajax方式发送。发送在界面显示上传状态,并把file类型的控件的值置空。在提交表单时,仅提交其他数据。

 

3在表单一次性的提交的,但是用了ajax。这种我没做通,但想想这种在现实中很少需要就没做了。

 

之后就说说一种可行的实现,使用了jquery file Upload。

Jquery file Upload(鉴于官方文档不是一般的乱,整理了一下关键的页面):

Hello world:http://bbs.9ria.com/thread-245293-1-1.html

官方文档:https://github.com/blueimp/jQuery-File-Upload/wiki

Option:https://github.com/blueimp/jQuery-File-Upload/wiki/Options

 

关于使用思路:

首先我比较关注上传的js部分,所以没去了解界面的部分。jquery file Upload应该是界面做得最好的。就上传插件而言,基本应该是配置,之后触发文件上传(怎么触发就是掌握不同插件的关键)

       jquery file Upload在这一点比较奇葩(不关注jquery file Upload可以跳过),它是默认点击input就自动上传的。它可以把上传触发绑定到一个事件上,通过add这个option来配置。但这其实是有问题的,比如表单有两个上传域,希望通过某按键的click事件触发上传,click事件的处理就会被调用两次。结论很简单,用jquery file Upload,不要把多个文件的上传绑在一起,让他们各自完成。

       jquery file Upload每次只会像服务器发一个文件,开始我也非常疑惑。但后来发现其他插件也是这样的。还是上面的一条,让每个上传各自完成。

 

其他好用的上传插件还有

Plupload:功能上最强,有校验,上传多个,拖拽上传等功能。(file类型域不是自己写的,通过指定一个button,由它的点击事件生成,通过调函数来开始上传)

文档:http://www.cnblogs.com/2050/p/3913184.html#plupload_doc4

Demo:http://chaping.github.io/plupload/demo/index.html

 

二服务端

客户端怎么做在服务端的接口都是一样的。都是面对着这么一个表单,表单里有一些字段,还有一个到多个的上传附件。

 技术分享

纵观附件的管理,业务流程是这样的

 技术分享

上传要处理的问题

附件重名处理

上传类型检查、大小等限制

上传文件的位置规划

文件夹及安全性

附件数据库

编码问题

 

上传方案

1最基本,用request的inputstream读写

2用组件库,common-fileupload(例子里用了这个),这些组件库对request进行封装。

3用struct提供的上传功能

 

例子中的处理过程

用common-uploadfile,需要以下包。自己写了一个工具类,改进自

http://lohasle.iteye.com/blog/1604428

 技术分享

上传文件夹放在网站根目录,可以用工具类把所有附件都传到一个目录,也可以按表单filedname配置上传的位置。

上传的文件会以一个随机的名字保存,避免了附件重名。

用MineType检查文件类型,查询minetype可点击

http://www.cnblogs.com/newcj/archive/2011/08/10/2134305.html

先把文件传到一个临时文件夹,用fileItem对象读取文件信息,再重命名为随机名,传到指定文件夹。

 

fileUpload几个要注意的问题

fileUpload关键就是使用FileItem类,可以查文档

文件名,内容乱码:http://zsw4522006.iteye.com/blog/1470949

       首先文件在上传和下载过程中是按字节传的,编码由文件本身保存所以不会乱码。

       但文件名会有乱码,fileItem的getString也会有乱码,则主要是fileUpload默认的和现实的编码不一样。

common-fileupload对request进行封装,一旦我们按流的方式打开http请求,就不能用getParameter方法,而要用common-fileupload提供的接口访问表单

 

下载、删除和使用

当把“上传文件的位置规划”和“数据库存储”解决,后面的问题就差不多了。毕竟文件已经在那里了,它的信息也详细记录在数据库。实现详见代码

 

最后上代码

无ajax的附件表单,UploadAddInput.jsp

技术分享
 1 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
 2 <%
 3 String path = request.getContextPath();
 4 String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
 5 %>
 6 
 7 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
 8 <html>
 9   <head>
10     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
11     <title>上传文件</title>
12   </head>
13   
14   <body>
15  
16       <form name="upform" action="UploadAdd.jsp" method="POST" enctype="multipart/form-data"> 
17           <input type ="text" name="name"/><br/> 
18         <input type ="file" name="file1"/><br/> 
19         <input type ="file" name="file2"/><br/> 
20         <input type="submit" value="Submit" /><br/> 
21         <input type="reset" /> 
22       </form> 
23 
24   </body>
25 </html>
View Code

Upload实体类,Upload.java

技术分享
 1 package com.xcat.upload;
 2 
 3 import java.util.Date;
 4 
 5 public class Upload {
 6     private int id;
 7     private String fileName;
 8     private String description;
 9     private String loc;
10     private String mineType;
11     //单位KB;
12     private int fileSize;
13     private Date upTime;
14     private int owner;
15     public int getId() {
16         return id;
17     }
18     public void setId(int id) {
19         this.id = id;
20     }
21     public String getFileName() {
22         return fileName;
23     }
24     public void setFileName(String fileName) {
25         this.fileName = fileName;
26     }
27     public String getDescription() {
28         return description;
29     }
30     public void setDescription(String description) {
31         this.description = description;
32     }
33     public String getLoc() {
34         return loc;
35     }
36     public void setLoc(String loc) {
37         this.loc = loc;
38     }
39     public String getMineType() {
40         return mineType;
41     }
42     public void setMineType(String mineType) {
43         this.mineType = mineType;
44     }
45     public int getFileSize() {
46         return fileSize;
47     }
48     public void setFileSize(int fileSize) {
49         this.fileSize = fileSize;
50     }
51     public Date getUpTime() {
52         return upTime;
53     }
54     public void setUpTime(Date upTime) {
55         this.upTime = upTime;
56     }
57     public int getOwner() {
58         return owner;
59     }
60     public void setOwner(int owner) {
61         this.owner = owner;
62     }
63     
64 }
View Code

上传工具类,参考http://lohasle.iteye.com/blog/1604428,UploadUtil.java

技术分享
  1 package com.xcat.upload;
  2 
  3 import java.io.File;
  4 import java.util.Date;
  5 import java.util.HashMap;
  6 import java.util.List;
  7 import java.util.Map;
  8 import java.util.Random;
  9 
 10 import org.apache.commons.fileupload.servlet.ServletFileUpload;
 11 import org.apache.commons.fileupload.FileItem;  
 12 import org.apache.commons.fileupload.disk.DiskFileItemFactory; 
 13 import javax.servlet.http.HttpServletRequest;  
 14 
 15 public class UploadUtil {
 16     public static class PosConfig{
 17         private String defaultPos;
 18         private Map<String,String> pos =new HashMap<String,String>();
 19         public String getDefaultPos() {
 20             return defaultPos;
 21         }
 22         public void setDefaultPos(String defaultPos) {
 23             this.defaultPos = defaultPos;
 24         }
 25         public Map<String, String> getPos() {
 26             return pos;
 27         }
 28         public void setPos(Map<String, String> pos) {
 29             this.pos = pos;
 30         }
 31         
 32     }
 33     /*
 34      * 常量
 35      */
 36     //一兆的大小   
 37     public static final long TRILLION = 1024 * 1024;
 38     //文件限制性的大小(默认的5M)
 39     public static final long DEFAULT_SIZE = TRILLION * 5;
 40     //试探随机文件名的最大次数
 41     public static final int RANDOM_NAME_TIMES =2000;
 42     
 43     /*
 44      * 参数
 45      */
 46     //限制的文件类型  
 47     private String[] allowMineType;
 48     // 限制的文件大小,默认5M,-1就是无限制大小
 49     private long fileMaxSize = DEFAULT_SIZE;
 50     // 设置缓冲区文件夹
 51     private String cachePathString;
 52     // 设置缓冲区大小,默认5M
 53     private long cacheSize = DEFAULT_SIZE;
 54  // 客户端文件系统字符编码
 55     private String clientCharset = "utf-8";
 56     // request表单的字符编码
 57     private String formCharset = "utf-8";
 58    
 59     //上传位置配置
 60     private PosConfig posConfig;
 61     
 62     
 63     /*
 64      * 变量
 65      */
 66     //request
 67     private HttpServletRequest request;
 68     // 文件上传处理类  
 69     private ServletFileUpload sfu;
 70     // 磁盘工厂 
 71     private DiskFileItemFactory factory = new DiskFileItemFactory(); 
 72     // 表单域的值
 73     private Map<String, String> formMap = new HashMap<String, String>(); 
 74     // 上传文件信息
 75     private Map<String, Upload> fileMap = new HashMap<String, Upload>();
 76     //上传的附件是否传到多个位置
 77     private boolean multiPos = false;
 78     
 79     public UploadUtil() {
 80     }
 81 
 82     public Map<String, String> getFormMap() {
 83         return formMap;
 84     }
 85 
 86     public void setFormMap(Map<String, String> formMap) {
 87         this.formMap = formMap;
 88     }
 89 
 90     public String getCachePathString() {
 91         return cachePathString;
 92     }
 93 
 94     public void setCachePathString(String cachePathString) throws Exception {
 95         this.cachePathString = cachePathString;
 96     }
 97 
 98     public long getCacheSize() {
 99         return cacheSize;
100     }
101 
102     public void setCacheSize(long cacheSize) {
103         this.cacheSize = cacheSize;
104     }
105 
106 
107     public long getFileSize() {
108         return fileMaxSize;
109     }
110 
111     public void setFileSize(long fileSize) {
112         this.fileMaxSize = fileSize;
113     }
114     
115     public String[] getAllowMineType() {
116         return allowMineType;
117     }
118 
119     public void setAllowMineType(String[] allowMineType) {
120         this.allowMineType = allowMineType;
121     }
122 
123     public HttpServletRequest getRequest() {
124         return request;
125     }
126 
127     public void setRequest(HttpServletRequest request) {
128         this.request = request;
129     }
130 
131     public PosConfig getPosConfig() {
132         return posConfig;
133     }
134 
135     public void setPosConfig(PosConfig posConfig) {
136         this.posConfig = posConfig;
137     }
138 
139     public String getClientCharset() {
140         return clientCharset;
141     }
142 
143     public void setClientCharset(String clientCharset) {
144         this.clientCharset = clientCharset;
145     }
146 
147     public String getFormCharset() {
148         return formCharset;
149     }
150 
151     public void setFormCharset(String formCharset) {
152         this.formCharset = formCharset;
153     }
154 
155     public Map<String, Upload> getFileMap() {
156         return fileMap;
157     }
158 
159     public void setFileMap(Map<String, Upload> fileMap) {
160         this.fileMap = fileMap;
161     }
162 
163     public ServletFileUpload getServletFileUpload() {
164         if (sfu == null) {  
165             return sfu = new ServletFileUpload(factory);
166         } else {
167             return sfu;
168         }
169     }
170     
171     /**
172      * // 得到用户ip地址
173      *
174      * 
175      * @return ip地址
176      */
177     public String getIpAddress() { 
178         String ip = request.getHeader("x-forwarded-for"); 
179         if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 
180           ip = request.getHeader("Proxy-Client-IP"); 
181         } 
182         if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 
183           ip = request.getHeader("WL-Proxy-Client-IP"); 
184         } 
185         if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 
186           ip = request.getHeader("HTTP_CLIENT_IP"); 
187         } 
188         if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 
189           ip = request.getHeader("HTTP_X_FORWARDED_FOR"); 
190         } 
191         if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 
192           ip = request.getRemoteAddr(); 
193         } 
194         return ip; 
195     }
196     /**
197      * // 得到一个随机文件名
198      *
199      * 
200      * @return 文件名
201      */
202     public String getRandomFileName(){
203         String str="";
204         
205         String[] ips=getIpAddress().split(".");
206         int ipv=0;
207         for(int i=0;i<ips.length;i++) ipv+= Integer.parseInt(ips[i]) << (8*i);
208         
209         str+=String.format("%010d", ipv);
210         str+=new Date().getTime();
211         Random r = new Random();
212         str+=String.format("%03d", r.nextInt(999));
213         return str;
214     }
215     
216     /**
217      * // 保证目录存在
218      *
219      * 
220      * @return
221      * @throws Exception 
222      */
223     public void ensureDir(String dir) throws Exception{
224         File file =new File(dir);
225         //如果文件夹不存在则创建 
226         if(!file.exists()  && !file.isDirectory()) 
227             if(!file.mkdirs()) throw new Exception("创建文件夹"+dir+"失败");
228     }
229     
230     /** 
231      * // 得到文件类型 
232      *  
233      * @param file 
234      * @return 文件后缀
235      */  
236     private String getFileExt(File file) {  
237         return getFileExt(file.getName());
238     }
239     
240     /** 
241      * // 得到文件类型 
242      *  
243      * @param fileName 
244      * @return 文件后缀
245      */  
246     private String getFileExt(String fileName) {
247         return fileName.substring(fileName.lastIndexOf(".") + 1);  
248     }
249     
250     /*
251      * //根据路径和扩展名,在改路径下获得一个可用的文件名
252      * @param dir:路径,ext:扩展名 
253      * @return 文件对象
254      * 
255      */
256     public File getRandomFile(String dir,String ext) throws Exception{
257         File file;
258         for(int i=0; i< RANDOM_NAME_TIMES ;i++){
259             file = new File(dir,getRandomFileName()+"."+ext);
260             if (!file.exists()) return file;
261         }
262         throw new Exception("上传繁忙");
263     }
264     
265     /*
266      * //判断mineType是不是在allowMineType中
267      * @param mineType 
268      * @return 
269      * 
270      */
271     public boolean validateMineType(String mineType) {
272         if (allowMineType == null) {  
273             return true;  
274         }  
275         for (int i = 0, len = allowMineType.length; i < len; i++) {  
276             if (allowMineType[i].equalsIgnoreCase(mineType)) {  
277                 return true;  
278             }  
279         }  
280         return false;  
281     }
282   
283     /*
284      * //所有表单附件都上传到dir
285      * @param dir 
286      * @return 
287      * 
288      */
289     public void uploadFiles(String dir) throws Exception {
290         multiPos=false;
291         posConfig =new PosConfig();
292         posConfig.defaultPos=dir;
293         uploadFiles();
294     }
295     
296     /*
297      * //按照路径配置来上传
298      * @param posConfig 
299      * @return 
300      * 
301      */
302     public void uploadFiles(PosConfig posConfig) throws Exception {
303         multiPos=true;
304         this.posConfig=posConfig;
305         uploadFiles();
306     }
307     
308     /** 
309      * 文件上传 参数urlString是具体指定的目录 如果该对象属性值为空 
310      * 将不使用缓存,无文件类型限制,上传大小默认为5M,目录规则默认为没有目录递归创建 相同文件名将覆盖源文件 
311      * 此方法如文件上传错误或者文件类型不匹配将抛出异常 
312      *  
313      * @param request 
314      *            当前请求 
315      * @param urlString 
316      *            urlString是具体指定的目录 
317      */ 
318     private void uploadFiles() throws Exception {  
319         if (!ServletFileUpload.isMultipartContent(request)) return;
320         ensureDir(cachePathString);
321         if(posConfig.defaultPos!=null)
322             ensureDir(posConfig.defaultPos);
323         
324         ServletFileUpload sfu = getServletFileUpload();
325         sfu.setHeaderEncoding(clientCharset);
326         sfu.setFileSizeMax(fileMaxSize);
327         if (cachePathString != null) {
328             factory.setRepository(new File(cachePathString));
329             factory.setSizeThreshold((int) cacheSize);
330         }
331         List<FileItem> items = sfu.parseRequest(request);
332         
333         for (FileItem ft : items) {
334             if (!ft.isFormField()) {
335                 if(allowMineType!=null && !validateMineType(ft.getContentType())) 
336                     throw new Exception("上传文件类型错误");
337                 else{
338                     String dir;
339                     if(multiPos && posConfig.pos.containsKey(ft.getFieldName())) {
340                         dir=posConfig.pos.get(ft.getFieldName());
341                         ensureDir(dir);
342                     }
343                     else dir= posConfig.defaultPos;
344                     File file = getRandomFile(dir,getFileExt(ft.getName()));
345                     ft.write(file);
346                     
347                     Upload upload=new Upload();
348                     upload.setFileName(ft.getName());
349                     upload.setLoc(file.getAbsolutePath());
350                     upload.setMineType(ft.getContentType());
351                     upload.setFileSize((int)(file.length()/1024));
352                     upload.setUpTime(new Date());
353                     fileMap.put(ft.getFieldName(), upload);
354                 }
355                 ft.delete();
356                 
357             } else {
358                 formMap.put(ft.getFieldName(), ft.getString(formCharset));
359             }
360         }
361     }  
362 }  
View Code

上传的业务,UploadAdd.jsp

技术分享
 1 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
 2 <%@ page import="com.xcat.upload.UploadUtil" %>
 3 <%@ page import="com.xcat.upload.Upload" %>
 4 <%@ page import="com.xcat.upload.UploadDao" %>
 5 <%
 6 out.clear();
 7 UploadUtil fu = new UploadUtil();
 8 fu.setRequest(request);
 9 fu.setCachePathString(request.getSession().getServletContext().getRealPath("/images/temp")); 
10 fu.setAllowMineType(new String[]{"image/jpeg","image/gif","text/plain","application/msword"});
11 try {
12     //全部上传到同一个目录
13     //fu.uploadFiles(request.getSession().getServletContext().getRealPath("/images/test"));
14     UploadUtil.PosConfig posConfig= new UploadUtil.PosConfig();
15     posConfig.setDefaultPos(request.getSession().getServletContext().getRealPath("/images/default"));
16     posConfig.getPos().put("file1", request.getSession().getServletContext().getRealPath("/images/test1"));
17     posConfig.getPos().put("file2", request.getSession().getServletContext().getRealPath("/images/test2"));
18     fu.uploadFiles(posConfig);  
19 } catch (Exception e1) {  
20     e1.printStackTrace();  
21 } 
22 
23 
24 Map<String,String> fieldMap = fu.getFormMap();
25 Set<String> key1 = fieldMap.keySet(); 
26 for(String s:key1){  
27     System.out.print("表单名称:"+s + "; ");  
28     System.out.println("表单值:"+fieldMap.get(s));  
29 }
30 
31 UploadDao uploadDao=new UploadDao();
32 Map<String,Upload> fileMap = fu.getFileMap();  
33 Set<String> key2 = fileMap.keySet();  
34 for(String s:key2){
35     
36     System.out.print("表单名称:"+s + "; ");  
37     System.out.println("文件名:"+fileMap.get(s).getFileName() + "; "+
38             "文件路径:"+fileMap.get(s).getLoc() + "; "+
39             "文件类型:"+fileMap.get(s).getMineType() + "; ");  
40     
41     Upload upload=fileMap.get(s);
42     
43     upload.setDescription("");
44     upload.setOwner(0);
45     uploadDao.add(upload);
46 }
47 
48 
49 out.print("[{\"name\":\"file.jpg\"}]");
50 %>
View Code

下载,DownLoad.jsp

技术分享
 1 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
 2 <%@ page import="com.xcat.upload.UploadDao" %>
 3 <%@ page import="com.xcat.upload.Upload" %>
 4 <%@ page import="java.io.File" %>
 5 <%@ page import="java.io.OutputStream" %>
 6 <%@ page import="java.io.FileInputStream" %>
 7 <%@ page import="java.net.URLEncoder" %>
 8 <%
 9 int id=Integer.parseInt(request.getParameter("id"));
10 UploadDao uploadDao=new UploadDao();
11 Upload upload=uploadDao.loadById(id);
12 if(upload==null) System.out.print("下载文件不存在");
13 File file=new File(upload.getLoc());
14 
15 response.reset();
16 response.setContentType(upload.getMineType());
17 String fileLoc = upload.getLoc();
18 String fileName = upload.getFileName();
19 fileName = URLEncoder.encode(fileName,"UTF-8");
20 response.addHeader("Content-Disposition","attachment;filename=" + fileName); 
21 response.setHeader("Content_length", String.valueOf(file.length())); 
22 
23 OutputStream outp = null;
24 FileInputStream in = null;
25 try
26 {
27     outp = response.getOutputStream();
28     in = new FileInputStream(fileLoc);
29 
30     byte[] b = new byte[1024];
31     int i = 0;
32 
33     while((i = in.read(b)) > 0)
34     {
35         outp.write(b, 0, i);
36     }
37     
38     outp.flush();
39     response.flushBuffer();  
40     out.clear();  
41     out = pageContext.pushBody(); 
42 }
43 catch(Exception e)
44 {
45     System.out.println("下载过程中出现读写错误!");
46     e.printStackTrace();
47 }
48 finally
49 {
50     if(in != null)
51     {
52         in.close();
53         in = null;
54     }
55     if(outp != null)
56     {
57         outp.close();
58         outp = null;
59     }
60 }
61 
62 %>
View Code

ajax上传客户端代码,UploadAddInputAjax.jsp

技术分享
 1 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
 2 <%
 3 String path = request.getContextPath();
 4 String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
 5 %>
 6 
 7 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
 8 <html>
 9   <head>
10     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
11     <title>上传文件</title>
12     <script src="style/js/jquery_1.9.1.js"></script>
13     <script src="style/js/jquery.ui.widget.js"></script>
14     <script src="style/js/jquery.iframe-transport.js"></script>
15     <script src="style/js/jquery.fileupload.js"></script>
16   </head>
17   
18   <body>
19  
20       <form name="upform" action="UploadAdd.jsp" method="POST" enctype="multipart/form-data"> 
21           姓名:<input type ="text" name="name"/><br/> 
22           年龄:<input type ="text" name="age"/><br/> 
23                   上传1:<input type ="text" id="file1_ct"/><input type ="file" name="file1" id="file1"/><br/>
24                   上传2:<input type ="text" id="file2_ct"/><input type ="file" name="file2" id="file2"/><br/>
25         <input type="submit" value="Submit" /><br/> 
26         <input type="reset" /> 
27       </form>
28        <script type="text/javascript">            
29            $("input[type=file]").fileupload({
30                 url:"UploadAdd.jsp",//文件上传地址,当然也可以直接写在input的data-url属性内
31                 dataType: json,
32                 formData: null,
33                 multi: true,
34                 done:function(e,result){
35                     //返回的数据在result.result中,假设我们服务器返回了一个json对象
36                     //console.log(JSON.stringify(result.result));
37                     $("#"+$(this).attr("id")+"_ct").val(result.result[0].name);
38                 }
39             });
40       
41        </script>
42 
43   </body>
44 </html>
View Code

 

 

参考文章

fileUpload×2:http://developer.51cto.com/art/200907/133797.htm

http://lohasle.iteye.com/blog/1604428

smartUpload:http://www.cnblogs.com/elleniou/archive/2012/09/24/2700583.html

文件夹操作:http://www.open-open.com/lib/view/open1384162223821.html

 

jsp文件上传

标签:

原文地址:http://www.cnblogs.com/deliver/p/4780854.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!