文件上传下载

文件上传下载

文件上传

文件上传的应用
比如个人信息的管理,上传头像
比如商品信息的管理,上传商品的图片
这些都需要通过浏览器客户端将图片上传到服务器的磁盘上

文件上传原理

所谓的文件上传就是服务器端通过request对象获取输入流,将浏览器端上传的数据读取出来,保存到服务器端

客户端浏览器注意事项

  • 请求方式必须是 post
  • 需要使用组件<input type="file" name="file">
  • 表单必须设置enctype="multipart/form-data"
    服务器端处理 通过request对象,获取InputStream, 可以将浏览器提交的所有数据读取到.

上传开源框架-commons-upload

Apache 开源组织提供了一个用来处理表单文件上传的一个开源组件( Commons-fileupload),该组件性能优异,并且其API使用极其简单,可以让开发人员轻松实现web文件上传功能,因此在web开发中实现文件上传功能,通常使用Commons-fileupload组件实现。

使用Commons-fileupload组件实现文件上传,需要导入该组件相应的支撑jar包:Commons-fileupload和commons-io, commons-io 不属于文件上传组件的开发jar文件,但Commons-fileupload 组件从1.1版本开始,它工作时需要commons-io包的支持jar包路径:

保存一个完整的表单信息,除了将文件上传到服务器以外,还需要将其他表单项信息保存到对象,并存储到数据中心,而对应的图片信息则保存为保存的路径即可。
那么怎么获取到其他的表单项数据?item.getFileName(),item.getString()
为了更好完成对其他表单项的值的获取,需要借助另一个开源框架 BeanUtils
它除了自身的jar包,还依赖于commons-logging.jar

所需要的包

文件上传案例

image

image

image

image

上传单个图片到磁盘

<body>
     <!-- enctype: 编码类型,提交的表单为二进制内容:multipart/form-data
                    默认为文本编码类型: application/x-www-form-urlencoded
          注意: 表单中的参数就不能通过getParameter去取值了, 要想取表单中的值需要第三方jar包解析    ;
                    但是action属性中的参数是可获取到的     
      -->
     <form action="RegisterServlet?action=upload" method="post" enctype="multipart/form-data">
        
        用户名:<input type="text" name="username" /><br/>
        密码:<input type="text" name="password" /><br/>
        文件上传:<input type="file" name="file" /><br/>
        <input type="submit" value="提交" />
     </form>
</body>
//版本1:解析图片,存储到D:/b.jpg
    private void deal1(HttpServletRequest request) throws IOException, FileNotFoundException {
        String action = request.getParameter("action");
        if("upload".equals(action)){
            
            //1. 实例化文件上传的工厂
            DiskFileItemFactory factory = new DiskFileItemFactory();
            
            //2.从工厂中获取fileUpload的对象
            ServletFileUpload fileUpload = new ServletFileUpload(factory);
            
            //3.通过fileUpload对象解析request
            try {
                //解析request后,将内容放入第三方提供的FileItem实体类中
                List<FileItem> list = fileUpload.parseRequest(request);
                //遍历集合,取出FileItem实体类中的数据
                for(FileItem fileItem : list){
                    //System.out.println(fileItem);  //打印FileItem实体类对象
                    //判断是否为文本表单内容
                    if(!fileItem.isFormField()){  //处理非文本表单内容---处理图片
                        
                        //-------io流操作-------
                        InputStream iStream = fileItem.getInputStream();
                        int len;
                        byte[] b = new byte[1024];
                        FileOutputStream fos = new FileOutputStream("d://"+fileItem.getName());
                        while((len=iStream.read(b))!=-1){
                            fos.write(b, 0, len);
                        }
                        //----------关流s----------
                        iStream.close();
                        fos.close();
                    }
                }
                
                
            } catch (FileUploadException e) {
                e.printStackTrace();
            }
            
        }
    }

优化版

//版本2:io流的优化;图片的路径要跟着项目走--不能随意指定:d:/a.jpg
    private void deal2(HttpServletRequest request) throws IOException, FileNotFoundException {
        String action = request.getParameter("action");
        if("upload".equals(action)){
            
            //1. 实例化文件上传的工厂
            DiskFileItemFactory factory = new DiskFileItemFactory();
            
            //2.从工厂中获取fileUpload的对象
            ServletFileUpload fileUpload = new ServletFileUpload(factory);
            
            //3.通过fileUpload对象解析request
                try {
                    List<FileItem> list = fileUpload.parseRequest(request);
                    for(FileItem fileItem : list){
                        if(!fileItem.isFormField()){  
                            //图片的路径要跟着项目走--不能随意指定:d:/a.jpg
                            //io流的优化
                            
                            //获取的是项目的部署路径下的upload
                            String path = getServletContext().getRealPath("upload");
                            //判断部署的upload路径是否存在,如果不存在则创建
                            File parent = new File(path);
                            if(!parent.exists()){
                                parent.mkdirs();   //创建父目录
                            }
                            
                            //拼完整图片的文件路径
                            File allFile = new File(parent, fileItem.getName());
                            
                            
                            //------------io流操作:第三方处理-----------
                            //参数1:上传的文件资源
                            IOUtils.copy(fileItem.getInputStream(), new FileOutputStream(allFile));
                        }
                    }
                    
                } catch (FileUploadException e) {
                    e.printStackTrace();
                }
        }
    }

解决重名的问题

//版本3:处理文件上传的重名问题?  uuid,时间
    private void deal3(HttpServletRequest request) throws IOException, FileNotFoundException {
        String action = request.getParameter("action");
        if("upload".equals(action)){
            
            //1. 实例化文件上传的工厂
            DiskFileItemFactory factory = new DiskFileItemFactory();
            
            //2.从工厂中获取fileUpload的对象
            ServletFileUpload fileUpload = new ServletFileUpload(factory);
            
            //3.通过fileUpload对象解析request
            
            List<FileItem> list;
            try {
                list = fileUpload.parseRequest(request);
                for(FileItem fileItem : list){
                    if(!fileItem.isFormField()){  
                        //图片的路径要跟着项目走--不能随意指定:d:/a.jpg
                        //io流的优化
                        
                        //获取的是项目的部署路径下的upload
                        String path = getServletContext().getRealPath("upload");
                        
                        //处理日期
                        SimpleDateFormat sdf = new SimpleDateFormat("/yyyy/MM/dd/HH/mm/ss");
                        String strDate = sdf.format(new Date());
                        
                        //判断部署的upload路径是否存在,如果不存在则创建
                        File parent = new File(path+strDate);
                        if(!parent.exists()){
                            parent.mkdirs();   //创建父目录
                        }
                        
                        //--处理重命名方式1: uuid
                        //String uuid = UUID.randomUUID().toString();
                        //String fileName = uuid+"_"+fileItem.getName();
                        //File allFile = new File(parent, fileName);
                        
                        //处理重命名方式2: 日期处理: 2019/3/11/11/38/56/a.jpg  2019/3/11/11/38/57/a.jpg
                        File allFile = new File(parent, fileItem.getName());
                        
                        IOUtils.copy(fileItem.getInputStream(), new FileOutputStream(allFile));
                    }
                }
                
            } catch (FileUploadException e) {

                e.printStackTrace();
            }
                    
        }
    }

上传文件并使用BeanUtils保存checkbox表单

<body>
     <!-- enctype: 编码类型,提交的表单为二进制内容:multipart/form-data
                    默认为文本编码类型: application/x-www-form-urlencoded
          注意: 表单中的参数就不能通过getParameter去取值了, 要想取表单中的值需要第三方jar包解析    ;
                    但是action属性中的参数是可获取到的     
      -->
     <form action="RegisterServlet?action=upload" method="post" enctype="multipart/form-data">
        
        用户名:<input type="text" name="username" /><br/>
        密码:<input type="text" name="password" /><br/>
        爱好:<input type="checkbox" name="loves" value="eat" />吃
            <input type="checkbox" name="loves"  value="drink"/>喝
            <input type="checkbox" name="loves"  value="play"/>玩<br/>
        文件上传:<input type="file" name="file" /><br/>
        <input type="submit" value="提交" />
     </form>
</body>
    //版本4:在后台存储完整数据
    private void deal4(HttpServletRequest request)
            throws UnsupportedEncodingException, IOException, FileNotFoundException {
        //1. 实例化文件上传的工厂
        DiskFileItemFactory factory = new DiskFileItemFactory();
        
        //2.从工厂中获取fileUpload的对象
        ServletFileUpload fileUpload = new ServletFileUpload(factory);
        
        //3.通过fileUpload对象解析request
        
        List<FileItem> list;
        User user = new User();
        Map<String, String> map = new HashMap<>();
        try {
            list = fileUpload.parseRequest(request);
            for(FileItem fileItem : list){
                if(fileItem.isFormField()){  //处理文本表单内容
                    
                    //获取到map集合中key对应的值,如果为空,则进行存储;否则,进行与旧值拼接
                    String value = map.get(fileItem.getFieldName());
                    if(value == null){
                        map.put(fileItem.getFieldName(), fileItem.getString("utf-8"));
                    }else{
                        map.put(fileItem.getFieldName(), value+","+fileItem.getString("utf-8"));
                    }
                    
                    
                }else{    //处理二进制内容
                    //获取的是项目的部署路径下的upload
                    String path = getServletContext().getRealPath("upload");
                    
                    //判断部署的upload路径是否存在,如果不存在则创建
                    File parent = new File(path);
                    if(!parent.exists()){
                        parent.mkdirs();   //创建父目录
                    }
                    
                    //--处理重命名方式1: uuid
                    String uuid = UUID.randomUUID().toString();
                    String fileName = uuid+"_"+fileItem.getName();
                    File allFile = new File(parent, fileName);
                    
                    IOUtils.copy(fileItem.getInputStream(), new FileOutputStream(allFile));
                    
                    //user.setUrl(allFile.getAbsolutePath());  //存储图片路径
                }
            }
            
        } catch (FileUploadException e) {

            e.printStackTrace();
        }
        //将map的数据注入的User对象,["username":"zs","password":"123"]
        try {
            BeanUtils.populate(user, map);
            
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }

展示单个图片、展示多个图片

    //版本5:展示单个图片、展示多个图片
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String action = request.getParameter("action");
        if("upload".equals(action)){
            
            DiskFileItemFactory factory = new DiskFileItemFactory();
            ServletFileUpload fileUpload = new ServletFileUpload(factory);
            
            List<FileItem> list;
            User user = new User();
            Map<String, String> map = new HashMap<>();
            try {
                list = fileUpload.parseRequest(request);
                for(FileItem fileItem : list){
                    if(fileItem.isFormField()){  //处理文本表单内容
                        
                        //获取到map集合中key对应的值,如果为空,则进行存储;否则,进行与旧值拼接
                        String value = map.get(fileItem.getFieldName());
                        if(value == null){
                            map.put(fileItem.getFieldName(), fileItem.getString("utf-8"));
                        }else{
                            map.put(fileItem.getFieldName(), value+","+fileItem.getString("utf-8"));
                        }
                        
                        
                    }else{    //处理二进制内容
                        //获取的是项目的部署路径下的upload
                        String path = getServletContext().getRealPath("upload");
                        
                        //判断部署的upload路径是否存在,如果不存在则创建
                        File parent = new File(path);
                        if(!parent.exists()){
                            parent.mkdirs();   //创建父目录
                        }
                        
                        //--处理重命名方式1: uuid
                        String uuid = UUID.randomUUID().toString();
                        String fileName = uuid+"_"+fileItem.getName();
                        File allFile = new File(parent, fileName);
                        
                        IOUtils.copy(fileItem.getInputStream(), new FileOutputStream(allFile));
                        
                        //注意:此处不能存带盘符的完整路径;应该存相对路径
                        //user.setUrl(allFile.getAbsolutePath());  //存储图片路径
                        //需求1: 存储单张图片路径
                        //user.setUrl("upload"+File.separator+fileName);  //File.separator 获取路径符号
                        //需求2: 存储多张图片路径
                        user.getUrList().add("upload"+File.separator+fileName);
                    }
                }
                
            } catch (FileUploadException e) {

                e.printStackTrace();
            }
            //将map的数据注入的User对象,["username":"zs","password":"123"]
            try {
                BeanUtils.populate(user, map);
                
                request.setAttribute("urls", user.getUrList());
                request.getRequestDispatcher("show2.jsp").forward(request, response);
                
            } catch (IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }
                    
        }
    }

文件下载

主要实现方式有两种

1.  超链接下载
2.  以超链接的方式下载压缩文件
3.  以超链接的方式下载图片文件
4.  以超链接的方式下载中文文件名的文件
5.  Servlet下载

有些超链接搞不定的事情就交给Servlet来搞定

采用流的方式来读和写

设置响应的头部信息

response.setHeader("Content-disposition", "attachment;fileName="+fileName);
细节:

下载的文件名是中文怎么办?

代码

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
      <!-- 英文路径直接下载 -->
      <a href="download/b.jpg">下载b.jpg</a><br/>
      <a href="download/b.rar">下载b.rar</a><br/>
      
      <!-- 带中文路径 -->
      <a href="DownloadServlet?path=download/高圆圆.rar">下载高圆圆.rar</a><br/>
</body>
</html>
package com.qf.servlet;

import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;

public class DownloadServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
       
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String path = request.getParameter("path");
        path = new String(path.getBytes("iso-8859-1"), "utf-8");  //download/高圆圆.jar
        
        //注意: 往往上传或下载的图片名不要有中文,且不要以数字开头
        
        //获取文件名
        String fileName = path.substring(path.indexOf("/")+1);
        
        //将中文文件名进行转码,类似cookie应用
        fileName = URLEncoder.encode(fileName, "utf-8");
        
        //设置响应的头部信息
        response.setHeader("Content-disposition", "attachment;fileName="+fileName);
        
        //注意:io流的读写,是针对完整路径
        path = getServletContext().getRealPath(path);
        //通过第三方方式写出去
        IOUtils.copy(new FileInputStream(path), response.getOutputStream());
    }


    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

}

总结:

1. 文件上传案例:
   1. 导入jar包:Commons-fileupload和commons-io
   2. 客户端的操作:
        1.表单的请求方式:method=post
        2.设置enctype=multipart/form-data
        3.控件类型: type="file"
   3. 服务器端的操作:
        1.创建DiskFileItemFactory
        2.创建ServletFileUpload
        3.通过ServletFileUpload的parseRequest方法得到所有的FileItem   
    4.  遍历集合中的所有FileItem对象
        判断如果为非文本表单内容,则将该图片通过io流方式取出
2. 文件上传操作及优化----版本升级
   1. 获取到FileItem对象后,取出io流内容----先读后写
   2. 存储图片路径跟着项目走(存到部署目录下);第三方io操作优化
   3. 处理文件名重名问题:  加前缀uuid、加入时间目录
   4. 存储完整表单数据: 文本表单、二进制数据等
         建立实体类---存图片时,只需存图片路径即可  
                               存文本表单时,需通过第三方jar包去注入
         导入jar包:    BeanUtils、commons-logging.jar                        
    存复选框数据时,如何存?
    判断map集合中是否存储该key,如果有则拼接;   
   5、存单张图片、存多张图片(实体类中要有集合的属性用于存url路径)
3. 文件的下载:
   1.简单的图片下载可以通过超链接直接下载即可  
   2.如果超链接下载不了(例如中文图片名);通过servlet去处理
   servlet处理步骤: 获取到图片路径
                   get请求的转码
                                      提取图片的文件名用于设置头部信息,且仍需转码
                                      通过io流进行下载: IOUtils.copy                 

other

写相对路径报错


image

image

然后


image

说明web项目和java项目还是有区别的
image

[站外图片上传中...(image-7b84af-1552350602309)]


image

.

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,681评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,710评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,623评论 0 334
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,202评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,232评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,368评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,795评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,461评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,647评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,476评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,525评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,226评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,785评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,857评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,090评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,647评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,215评论 2 341

推荐阅读更多精彩内容