《JavaWeb学习总结》文件上传

文件上传是开发中常用的功能之一,在此学习过程中做个简单的总结!

一.文件上传概述:

  • 文件上传是什么?
    在web开发中,经常需要用户上传一些文件,比如上传头像,上传附件等
  • 文件上传步骤
    1.提供一个文件上传的表单
    2.在Servlet中接收处理上传的文件,保存到服务器中

二.文件上传实现

  • 表单页面
    1.文件上传input输入框必须要有一个name属性,不然不会被提交
    2.表单的提交方式必须是POST,GET方式有大小限制
    3.表单form必须具有属性enctype=”multipart/form-data”
<html>
<head>
     <title>文件上传测试</title>
</head>
<body>
    <%--注意:提交方式必须是POST,必须指定enctype="multipart/form-data",并且input输入框必须要有一个name属性--%>
    <form action="${pageContext.request.contextPath}/upload" method="post" enctype="multipart/form-data">
        <input type="text" name="name">上传者<br>
        <input type="file" name="file"><br>
        <input type="submit" value="上传">
    </form>
</body>
</html>
  • 服务器Servlet处理上传的数据
    1.方式一:
      通过Request对象提供的getInputStream()方法获取输入流,可以读取到客户端提交过来的数据,但是这种方式需要对流中获取到的数据进行处理,比较麻烦,不建议使用
    2.方式二:
      借助Apache组织提供的开源组件Commons-fileUpload,可以轻松实现web开发文件上传功能

三:方式二具体实现

1.准备两个架包

commons-fileupload-1.3.3.jar
commons-io-2.6.jar
链接: https://pan.baidu.com/s/193h2xI-p0ToSmPmlm65jsQ 密码: c772 包含源码

2.编程实现
- 1>创建DiskFileItemFactory对象,并且设定缓冲区大小和临时文件目录
  public DiskFileItemFactory(int sizeThreshold, File repository)  参数说明:sizeThreshold缓冲区大小  repository临时目录
  说明:因为文件上传的时候只能在全部实体内容读取完毕之后才能做处理,所以需要缓存起来,第一个参数设置内存缓存大小,内存缓存虽然快,但是大文件耗费内存,所以我们需要第二个参数设置磁盘缓冲,磁盘缓冲慢,但是可以存放大量数据。当数据大小小于sizeThreshold的时候使用内存缓存,当数据大于sizeThreshold的时候使用磁盘缓存
- 2>创建文件上传核心类ServletFileUpload
  public ServletFileUpload(FileItemFactory fileItemFactory)  参数说明:传入步骤一创建的DiskFileItemFactory对象
说明:核心类ServletFileUpload提供了很多方法,此处列举常用方法

  判断当前提交数据表单是否是一个enctype为multipart/form-data类型的表单
  boolean isMultipartContent(HttpServletRequest request)
  指定处理文件名时使用的编码集
  setHeaderEncoding(java.lang.String encoding)
  控制单个文件大小最大值
  setFileSizeMax(long fileSizeMax)
  控制总的文件大小最大值
  setSizeMax(long sizeMax)
  解析request对象 获取 FileItem的集合
  List parseRequest(HttpServletRequest request)
  设置上传文件监听器
  setProgressListener(ProgressListener pListener)

  • 3>判断当前表单是否是一个文件上传表单
      通过ServletFileUpload对象的isMultipartContent(HttpServletRequest request)方法区分一下当前请求表单是否是文件上传表单
  • 4>设置处理文件名的字符集,防止中文乱码(必需)
      通过ServletFileUpload对象的setHeaderEncoding(String encoding)方法设置
  • 5>解析Request对象,获取FileItem集合,执行具体逻辑
      5.1 获取FileItem集合:通过ServletFileUpload对象的parseRequest(HttpServletRequest request)方法解析Request对象
      5.2 对FileItem集合进行操作,获取表单提交过来的数据:先判断当前FileItem是否是普通表单内容,如果是,获取到内容并且在控制台做输出演示;如果不是,则说明是文件上传项,获取到文件内容输入流,通过对输入输出流的操作把文件写到磁盘指定路径下,写完后关闭输入输出流
  • 6>删除临时文件
      通过ServletFileUpload对象的delete()方法,必须要做的操作,不然服务器磁盘中会堆积一大堆临时文件
package file_upload;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadBase;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

@WebServlet("/upload")
public class UploadServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext sc = req.getServletContext();
        //1.创建DiskFileItemFactory对象,并且设定缓冲区大小和临时文件目录
        DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory(1024, new File(sc.getRealPath("temp")));
        //2.创建ServletFileUpload对象
        ServletFileUpload fileUpload = new ServletFileUpload(diskFileItemFactory);
        //3. 判断当前表单是否是一个文件上传表单
        if (!ServletFileUpload.isMultipartContent(req)){
            //如果不是正确的文件上传表单抛出异常
            throw new RuntimeException("请使用正确的表单上传文件!!");
        }
        //3.1设置单个文件上传的最大大小(如有需要)
        fileUpload.setFileSizeMax(1024*1024);//1M
        //3.2设置单次上传文件总大小(如有需要)
        fileUpload.setSizeMax(1024*1024*10);//10M
        //4.设置处理文件名的字符集,防止中文乱码(必需)
        fileUpload.setHeaderEncoding("UTF-8");
        //5.解析Request对象,获取FileItem集合,执行具体逻辑
        try {
            //5.1 获取FileItem集合
            List<FileItem> fileItems = fileUpload.parseRequest(req);
            //5.2 对FileItem集合进行操作,获取表单提交过来的数据
            if (fileItems!=null){
                for (FileItem fileItem : fileItems) {
                    //通过isFormField()方法判断当前FileItem是否是普通表单内容,true表示是,false表示否
                    if (fileItem.isFormField()){
                        //普通表单内容,仅演示输出在控制台
                        //获取input的name属性的值
                        String name = fileItem.getFieldName();
                        //获取input的value属性的值
                        //String value = fileItem.getString();//不建议使用,中文会乱码
                        String value = fileItem.getString("UTF-8");//指定编码防止乱码
                        System.out.println("name="+name+"|value="+value);
                    } else {
                        //文件上传项
                        //获取上传文件的文件名
                        String name = fileItem.getName();
                        //获取文件内容的流
                        InputStream in = fileItem.getInputStream();
                        //获取文件输出流(此处文件保存位置需要注意,后续解决)
                        FileOutputStream fos = new FileOutputStream(sc.getRealPath("upload")+"/"+name);
                        byte[] bytes = new byte[1024];//用作缓冲
                        int len = -1;
                        while ((len = in.read(bytes))!=-1){
                            fos.write(bytes,0 ,len );//写文件到磁盘
                        }
                        //写完关流
                        in.close();
                        fos.close();
                        //关流后删除临时文件(注意:一定要删除,不然会堆积很多临时文件)
                        fileItem.delete();
                    }
                }
            }
        } catch (FileUploadBase.FileSizeLimitExceededException e){
            //超出文件大小的时候抛出异常
            throw new RuntimeException("文件大小超过上限!!");
        } catch (FileUploadException e) {
            e.printStackTrace();
        }
    }
}

四. 文件上传中常见问题的处理

  • 浏览器兼容性问题
      问题:有浏览器提交的文件名是绝对路径,带有盘符,这将会导致服务器写文件的时候抛异常
      解决方案:对提交过来的文件名进行判断,如果带有”/”,则进行字符串切割处理,截取到我们需要的文件名就可以
if (name.contains("\\")){//此处需要转义
      name = name.substring(name.lastIndexOf("\\"));
}
  • 服务器文件保存路径问题
      问题:如果服务器保存上传文件的路径是用户可以通过浏览器访问到的,那么就会导致上传文件中的代码被执行,比如用户上传的是以下jsp文件(仅做示范,不要拿自己电脑测试),如果用户可以访问到,那么服务器C盘文件将会全部清空
<%@ page import="java.io.File" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <%
        File file = new File("C:");
        File[] files = file.listFiles();
        for (File f : files) {
            f.delete();
        }
    %>
</body>
</html>

  解决方案:WEB-INF文件夹写的内容对用户来说是不可见的,所以可以把上传的文件放到WEB-INF下

//前边代码修改为
FileOutputStream fos = new FileOutputStream(sc.getRealPath("/WEB-INF/upload")+"/"+name);
  • 服务器保存文件名重复问题
      问题:多个用户上传的文件名像同的话,会将前面上传的文件内容被覆盖
      解决方案:基于用户的上传时间+计算机时钟+ip地址等信息,生成一个独立无二的编码,拼接在文件名前面,这样可以保证不同用户的文件的文件名不重复,此处演示使用UUID对象随机生成一个字符串+文件名作为保存文件名
//前边代码修改为
name = UUID.randomUUID()+"_"+name;
  • 服务器单个文件夹下保存文件过多问题
      问题:如果把所有文件都存储在同一个文件夹下,后期用户想要获取文件的时候,如果文件过多,搜索将会耗费比较多的时间,影响用户体验。
      解决方案:应该对目录进行分离,目录分离的方案有很多,比如按时间分离、按用户分离、按个数分离、按目录分离算法。此处介绍按文件名的hashCode进行分离。
//将文件名的hashCode转变为16进制字符串
String hcStr=Integer.toHexString(name.hashCode());
// 不满8位补0
while(hcStr.length()<8){
    hcStr="0"+hcStr;
}
//拼接目录
String midPath="/";
for(int i=0;i<hcStr.length();i++){
    midPath+=hcStr.charAt(i)+"/";
}
//获取文件保存的文件夹的绝对路径
String savePath = sc.getRealPath("/WEB-INF/upload"+midPath);
//在服务器中创建对应文件夹
new File(savePath).mkdis();
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值