1.本质:I/O读写
    客户端(浏览器)发送文件 Http协议
    文件的内容写出去 客户端本地输入流(内容) 网络输出流
    服务器(Web容器)接受文件 协议
    文件的内容读过来 网络输入流(内容) 服务器本地输出流

    2.可以使用别人写好的包来进行文件的上传和下载
    常用的是apache组织提供的一个包
    commons-fileupload.jar http://commons.apache.org/proper/commons-fileupload/download_fileupload.cgi
    image.png
    commons-io.jar https://commons.apache.org/proper/commons-io/download_io.cgi
    image.png

    3.上传的步骤
    浏览器端
    ①写一个jsp/html做展示(选择某一个文件)
    ②必须通过表单提交
    ③必须使用post
    规定:ServletFileUpLoad类中一个属性 post
    字节数:
    get没有限制,肯定通过URL拼串
    不同浏览器对于URL处理长度有所不同
    post没有限制,需要web容器的支持,跟容器的处理性能有关
    ④以前我们使用表单时只有两个属性 action method
    如果想要做文件上传,表单中必须添加一个属性 enctype=”multipart/form-data”
    ⑤必须使用,通过这个组件让用户选择上传的文件
    不仅仅能得到文件名,文件内容也会读出来

    服务器端
    ①引入文件上传需要的jar包
    commons-fileupload-1.4.jar
    commons-io-2.6.jar
    ②使用一个DiskFileItemFactory对象,使用一个ServletFileUpload对象,使用upload对象解析request,得到一个List<FileItem>集合

    1. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    2. try {
    3. //创建一个工厂
    4. DiskFileItemFactory factory=new DiskFileItemFactory();
    5. ServletFileUpload upload=new ServletFileUpload(factory);
    6. //通过upload解析request对象,因为请求携带的信息都在request对象中
    7. List<FileItem> itemList= upload.parseRequest(req);
    8. }catch (Exception e){
    9. e.printStackTrace();
    10. }
    11. }

    ③自己的业务逻辑
    (我们用桌面充当浏览器端,D://Test充当服务器端)

    1. for(FileItem item:itemList){
    2. if(item.isFormField()){//是一个普通的组件
    3. //注意不能使用request.getParameter("key");来获取
    4. String key=item.getFieldName();//获取组件的name属性
    5. //String value=item.getString();获取组件的value属性
    6. String value=item.getString("UTF-8");//使用重载传递字符集处理字符问题
    7. System.out.println(key+"---"+value);
    8. }else{//是file文件
    9. String key=item.getFieldName();//获取组件的name属性
    10. String realFileName=item.getName();//获取上传文件的真实名字
    11. //如果传递的文件的真实名字中出现中文乱码问题,req.setCharacterEncoding("UTF-8");可以解决
    12. //我们用桌面充当浏览器端,D://Test充当服务器端
    13. item.write(new File("D://Test/"+realFileName));
    14. // InputStream is= item.getInputStream();//获取一个输入流,读取网络传递过来的文件内容
    15. //原生的方式实现文件的上传
    16. // OutputStream outputStream = new FileOutputStream("D://test/"+realFileName);
    17. // byte[] b = new byte[1024];
    18. // int length = inputStream.read(b);
    19. // while(length!=-1){
    20. // outputStream.write(b,0,length);
    21. // outputStream.flush();
    22. // length = inputStream.read(b);
    23. // }
    24. // outputStream.close();
    25. }
    26. }

    1234567.mp4 如果上传的文件比较大
    产生临时文件,为了防止丢包
    默认情况下临时文件存在Tomcat的temp文件夹中
    factory.setSizeThreshold(int);设置缓冲区大小,默认为10240
    factory.setRepository(new File(“路径”));设置缓冲区位置
    还可以设置上传文件本身大小:
    upload.setFileSizeMax(long);单个上传文件的大小
    upload.setSizeMax(long);上传文件的总大小

    文件上传时如果放在一个固定的位置,可移植性不好,一般放在当前工程的根目录下
    String path=this.getServletContext().getRealPath(“/“);获取根目录

    如果文件上传出现文件名冲突的问题?
    原来名字+用户+时间.原来后缀
    1_zzt_date.jpg
    用一个随机产生的名字当做保存在服务器中的文件名
    1.jpg—->xxxxx.jpg 1 xxxxx 路径 用户 上传时间
    一个随机名字创建一个文件夹
    1.jpg放在文件夹里 用户 路径 文件夹名

    有些时候可能上传的文件需要控制类型
    自己设计一个方法

    private boolean testFileType(String fileName){
            String[] box = {"txt","doc","pdf","jpg","png"};
            for(int i=0;i<box.length;i++){
                if(fileName.endsWith(box[i])){
                    return true;
                }
            }
            return false;
        }
    

    补充:
    有时上传时我们需要监督进度,可以通过upload对象来实现

    upload.setProgressListener(new ProgressListener() {
        @Override
        public void update(long l, long l1, int i) {
            //l表示已经上传的字节数(每4096个字节就会更新)
            //l1表示上传文件的总字节数
            //i表示正在上传第几个组件
        }
    });