第17天 笔记 文件上传下载

第17天 笔记 文件上传下载

  1. 使用过滤器的步骤
  1. filter执行顺序如何确定,如果有两个过滤器都拦截同一个请求。

3)Filter接口中的doFilter方法和FilterChain接口中的doFilter的区别

今日任务:

  1. 文件上传
  2. 文件下载

课堂笔记

1、文件上传

1.1、文件上传介绍

    生活中遇到的文件上传技术实现?

    网盘,头像,邮件的附件,朋友圈(日报)

    文件上传(B/S)架构和基本原理?

    B/S架构是浏览器和服务器架构,浏览器发出请求,服务器给出响应。

    C/S架构是客户端和服务器架构,客户端发出请求,服务器给出相应。

历史:以前都是喜欢客户端和服务器,第一,获取用户的计算机数据方便,对用户的控制力强,方便培养用户习惯,自定义网络传输的协议。

客户端,占用计算机内存和硬盘,客户端,使用协议太多,安全性不高,所以诞生浏览器

浏览器设计宗旨:丢弃客户端,占用内存和硬盘,统一协议,安全性高,使用浏览器,只需要更新浏览器即可。

使用B/S架构来做文件上传的思路?

  1. 给用户一个功能,让他自己可以去选择要上传的文件
  2. 需要让用户可以发送当前被上传文件的数据,而且,还要告诉服务器,这是文件上传的请求
  3. 接收请求,使用IO技术读取请求中被上传文件的数据
  4. 将数据保存到服务器的硬盘上。

1.2、文件上传页面实现和注意细节(重点:必须掌握)

    第一个问题:如何告诉浏览器,让用户选择要上传的文件?

    <!-- 演示在页面中显示选择文件上传的按钮 -->

     请选择要被上传的文件:<input type="file" name="upload">

    第二问题:如何提交数据?

    GET请求:GET请求数据长度有限制(1KB),所以不能用。

    POST请求:就用这个,这个好,数据长度没有限制。

    

    使用POST请求,一般是用form表单,设置请求方式为POST

    第三个问题:如何通知服务器,这是一个文件上传的请求?

    设置enctype(这是form表单中属性)的值为:multipart/form-data

思考:

文件上传的时候,我们数据,都是二进制的数据,不应该被编码

普通的请求,数据到服务器之后,我们需要的都是文本内容,需要被存入数据库,所以需要编码

MIME类型:文件的媒体类型,.Jpg .avi .rmvb .mp4 .3gp

现在很多视频软件,它的后缀名都是当前软件名称,爱奇艺,.aqy .itcast

我的视频,提供下载功能,也只能用自己的播放器看。

浏览器代码实现:

<!-- 设置一个form表单,设置请求方式为post,保证发送数据没有长度限制 -->

<!-- 设置enctype="multipart/form-data" 用来提示当前是文件上传请求 -->

<form action="${root }/upload1" method="post" enctype="multipart/form-data">

        <!-- 设置一个按钮,让用户可以选择要被上传的文件 -->

        请选择要上传的文件:<input type="file" name="upload">

        <input type="submit" value="确认上传">

</form>

小结(文件上传浏览器端实现):

  1. 设置一个<input type="file" name="upload">,让用户去选择要被上传的文件
  2. 设置一个form表单,method="POST"(让文件上传数据长度没有限制) ,enctype=" multipart/form-data"(告诉服务器这是一个文件上传的请求,数据不要编码)
  3. 设置一个提交按钮,提交请求。

文件上传浏览器效果:

1.3、文件上传的服务器简单实现

上传的数据在哪里?

浏览器的请求发送过来之后,服务器创建一个请求对象,将所有的请求数据,都封装在request对象中,

所以文件上传的数据,肯定在request对象中。

文件上传代码示例:

package cn.itcast.upload;

 

import java.io.IOException;

 

import javax.servlet.ServletException;

import javax.servlet.ServletInputStream;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

 

public class UploadServelt1 extends HttpServlet {

 

    public void doGet(HttpServletRequest request, HttpServletResponse response)

            throws ServletException, IOException {

 

        //文件上传请求的数据,肯定在request

        //第一步:获取上传的请求流

        ServletInputStream in = request.getInputStream();

        //第二步:将数据打印在控制台上

        int temp = 0;

        while((temp = in.read()) != -1){

            System.out.write(temp);

        }

    }

 

    public void doPost(HttpServletRequest request, HttpServletResponse response)

            throws ServletException, IOException {

        doGet(request, response);

    }

 

}

基本的IO操作:

    File : 文件和文件夹的操作

    Inputstream : 读取文件数据

    Outputstream:输出数据

文件上传原理分析:

-----------------------------15767295007281,一个数据的分隔符,

这个分隔符,将不同input输入框中的数据,进行分隔。

text/plain enctype(MIME类型),都是指文件的媒体类型

.avi .mp3 .txt .jpg 以后缀名的方式显示的媒体格式;

后缀名对应的文件媒体格式,text/plain这样的方式描述,如果需要找到后缀名相关的文件媒体格式

注意:文件名的后缀名和文件的媒体格式的对应关系

可以通过tomcat服务器目录下的conf文件夹中的web.xml文件来查询,查询搜索mime-mapping就可以了

重点:被上传文件的真实数据,其实都在text/plain这一行后,隔一行开始,一直到分隔符结束。

2、Apache的commons-fileupload工具jar包介绍

2.1、commons-fileupload介绍

    

下载:http://commons.apache.org/proper/commons-fileupload/

    

需要使用fileupload组件完成文件上传,这时还需要下载commons-io 的jar包。

    

解压,把jar包复制到项目中。

flieUpload工具的目录结构:

    

2.2、fileupload的文件上传解析流程图

因为,文件上传,必须,完成所有步骤才能看到效果,所以,先完成文件上传,然后,分许其中的对象和步骤。

普通字段: <input type="text" name="desc">——普通文本框

上传文件1:<input type="file" name="upload">——上传文件的组件

上传文件2:<input type="file" name="upload">——上传文件的组件

ServletFIleUpload: 它是解析文件上传数据的核心对象

DiskFileItemFactory: 它是帮助ServletFIleUpload一起解析request的对象,作用:将所有input输入框中的数据,解析,成一个一个的FileItem对象

FileItem:封装了所有input输入框中数据的对象

以上三个对象,是我们核心对象。

过程:

  1. 创建一个DiskFileItemFactory,将这个对象绑定到ServletFIleUpload
  2. 使用ServletFIleUpload对象,解析request
  3. 解析完request,获取的是一个一个的fileItem对象(input输入框中内容)

2.3、fileupload的实现演示(重点:必须掌握=必须自己代码写出来)

package cn.itcast.web;

 

import java.io.File;

import java.io.IOException;

import java.util.List;

 

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

 

import org.apache.commons.fileupload.FileItem;

import org.apache.commons.fileupload.FileUploadException;

import org.apache.commons.fileupload.disk.DiskFileItemFactory;

import org.apache.commons.fileupload.servlet.ServletFileUpload;

 

public class UploadServlet2 extends HttpServlet {

 

    public void doGet(HttpServletRequest request, HttpServletResponse response)

            throws ServletException, IOException {

 

        //需求:完成文件上传

        

        //1    创建一个DiskFileItemFactory,将这个对象绑定到ServletFIleUpload

        DiskFileItemFactory factory = new DiskFileItemFactory();

        ServletFileUpload fileUpload = new ServletFileUpload(factory);

        //2    使用ServletFIleUpload对象,解析request

        try {

            //3    解析完request,获取的是一个一个的fileItem对象(input输入框中内容)

            //解析动作已经完成。

            List<FileItem> list = fileUpload.parseRequest(request);

            

            //4遍历循环list集合,根据不同fileItem对象,不同处理

            for (FileItem item : list) {

                //item.isFormField() 判断当前是否是一个普通字段:true,是 false 不是

                if(item.isFormField()){

                    //表示普通字段

                    System.out.println(item.getFieldName());

                    System.out.println(item.getString());

                }else{

                    //表示文件上传

                    //5将被上传的文件数据,保存到服务器的硬盘上

                    //将被上传的文件统一保存在upload文件夹中

                    //C:/apache-tomcat-7.0.52/webapps/day17/upload

                    String realPath = this.getServletContext().getRealPath("/upload");

                    //6)获取文件名

                    String fileName = item.getName();

                    File file = new File(realPath,fileName);

                    try {

                        item.write(file);

                    } catch (Exception e) {

                        e.printStackTrace();

                    }

                }

            }

        } catch (FileUploadException e) {

            e.printStackTrace();

        }

    }

 

    public void doPost(HttpServletRequest request, HttpServletResponse response)

            throws ServletException, IOException {

        doGet(request, response);

    }

 

}

3、commons-fileupload 详解(对核心类的API介绍)

3.1、DiskFileItemFactory类(磁盘的文件项工厂类)

3.1.1、设置缓存大小

方法截图:

为什么要设置缓存大小?

缓存越大,效率越高。

缓存:内存中开辟的一个存储数据的空间

内存读取效率高于硬盘,设置缓存,提高读取效率,缓存越大,效率越高。

代码操作:

  1. 设置临时文件存储位置

方法截图:

为什么要设置临时文件存储位置?

被上传文件的大小,一定会出现超出缓存大小的情况,这些超出的数据,也需要找一个地方存储(临时)起来。

相当于:卡车拉了一堆砖头,需要一个临时的场地,存放这些砖头,为了后期,在建筑房子(形成文件),使用这些砖头。

总结:1)数据必然会超出缓存

     2)数据必须在形成文件之前临时存储

代码操作:

如何获取当前操作系统的临时文件存储位置?

System类

代码演示:

package cn.itcast.utils;

 

public class TestUtils {

 

    public static void main(String[] args) {

        //C:UserswjnAppDataLocalTemp

        System.out.println(System.getProperty("java.io.tmpdir"));

    }

}

3.2、ServletFileUpload类

3.2.1、parseRequest方法

方法截图:

定义:解析request对象,获取文件上传的数据的方法

代码操作:

3.2.2、isMultipartContent方法

方法截图:

定义:判断当前请求,是否,是一个文件上传的请求

代码操作:

//如果当前请求,不是文件上传

        boolean b = ServletFileUpload.isMultipartContent(request);

        //b true:表示是文件上传,false:不是文件上传

        if(!b){

            //不是文件上传

            response.sendRedirect(request.getContextPath());

            return;

        }

3.2.3、setHeaderEncoding方法

方法截图:

定义:解决中文文件名乱码问题

代码操作:

        

3.3、FileItem类

3.3.1、isFormField方法

方法截图:

定义:判断当前input输入框是否是一个普通字段,true,是,false,不是

代码操作:

3.3.2、getFieldName方法

方法截图:

定义:获取input输入框中name属性值

代码操作:

3.3.3、getString方法

方法截图:

定义:获取input输入框中,用户输入的值(value值)的方法

代码操作:

3.3.4、getName方法

方法截图:

定义:获取被上传文件的文件名

代码操作:

3.3.5、getInputStream方法(不推荐使用)

方法截图:

定义:

为什么不推荐使用getInputStream?

如果使用这个方法,先获取输入流,在获取输出流,继续写IO代码,代码太过繁琐,推荐使用原有的方法

item.write(new File(realPath,fileName));

代码操作:

//获取输入流

//                        InputStream in = item.getInputStream();

//                        //获取输出流

//                        FileOutputStream out = new FileOutputStream(new File(realPath,fileName));

//                        //设置缓存区

//                        byte[] buf = new byte[1024];

//                        int len = 0;

//                        while( (len = in.read(buf)) != -1 ){

//                            out.write(buf,0,len);

//                        }

3.3.6、delete方法

方法截图:

定义:删除临时文件的方法

临时文件会自动删除(item.write()),但是,如果发生异常,临时文件有可能没有删出,调用删除方法

代码操作:

4、多文件上传

4.1、浏览器端js实现

需求:让用户自己决定上传文件的input输入框的个数

思路:

  1. 提供一个功能给用户,让用户可以自己去添加input输入框
  2. 提供一个功能给用户,让用户可以自己去删除input输入框
  3. 提交数据按钮

实现:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<c:set var="root" value="${pageContext.request.contextPath }"/>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>

<head>

 

<title>My JSP 'index.jsp' starting page</title>

    <meta http-equiv="pragma" content="no-cache">

    <meta http-equiv="cache-control" content="no-cache">

    <meta http-equiv="expires" content="0">

    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">

    <meta http-equiv="description" content="This is my page">

    <script type="text/javascript">

        //用户自己添加input,用户点击一下,创建一个input

        function _addInput(){

            //创建一个input输入框

            var _input = document.createElement("input");

            //设置文件上传相关属性

            _input.setAttribute("type", "file");

            _input.setAttribute("name", "upload");

            //input标签存入div中(id="_un"

            var _div = document.getElementById("_un");

            _div.appendChild(_input);

              

              

            

            

            //再提供一个删除的按钮:点击一个删除按钮,删除一个input输入框

            var _delete = document.createElement("input");

            //设置删除按钮的相关属性

            _delete.setAttribute("type", "button");

            _delete.setAttribute("value", "删除");

            //设置删除按钮的onclick事件,事件(启动一个js函数,执行删除操作)

            _delete.onclick = function(){

                //执行删除的操作

                //删除的内容有:文件上传输入框,删除按钮本身,换行符

                //删除操作由父标签执行

                //this:代表了当前这个标签对象_delete

                var _f = this.parentNode;

                //删除文件上传输入框,先获取当前元素的前一个元素

                var _p = this.previousSibling;

                _f.removeChild(_p);

                

                //删除换行符,先获取当前元素的后一个元素

                var _n = this.nextSibling;

                _f.removeChild(_n);

                

                //自杀

                _f.removeChild(this);

                

            };

            

            _div.appendChild(_delete);

            

            //在每一个input输入框之后,加上一个换行符

            var _br = document.createElement("br");

            _div.appendChild(_br);

        }

      

    

    </script>

</head>

 

<body>

<!-- 设置一个按钮,用来启动js函数,创建文件上传的input输入框 -->

<input type="button" value="添加文件上传" onclick="_addInput();"><br/>

<form action="${root }/upload3" method="post" enctype="multipart/form-data">

    文件描述:<input type="text" name="desc"><br/>

<!-- 演示在页面中显示选择文件上传的按钮 -->

     请选择要被上传的文件:<input type="file" name="upload"><br/>

     <!-- 当前这个div的位置,就是input标签要存放的位置,那么input标签,只要存放在div中,就是放对了位置 -->

     <div id="_un">

     </div>

     <!-- 不要忘了添加表单的提交按钮 -->

     <input type="submit" value="文件上传">

</form>

</body>

 

 

 

</html>

在js变量命名时,加上下划线,是为了防止和js中本身全局变量或者函数起冲突。

4.2多文件上传问题

4.2.1、文件重名

重名的原因:

每次上传文件的时候,因为文件名相同,所以后上传的文件覆盖前一个文件。

解决方案(学生讨论15分钟,每个小组尽可能多的提供解决方案):

方案一:用户ip-文件名-(1).zip

文件名命名方式:使用用户id 加上 文件名 加上数字(文件出现多少次,数据就是几)

用户名如果保证用户名,每个都不同

IP 不是每个用户都有一个IP的,在局域网范围内,对外的IP是同一个。

如果有重复的文件名,在原来的文件名上,加上数字,每出现一次,加一。

不能擅自压缩用户的数据,那么用户重新从网盘下载数据的时候,发现文件后缀名不同。

方案二:添加当前文件的最后修改时间,文件的大小,文件前几KB的数据

方案三:UUID,可以解决

方案四:文件绝对路径+用户名 进行拼接 作为新的文件名

不可取,绝对路径会出现重复,而且文件上传只有IE Edge浏览器上传文件,才会带上绝对路径。

上传文件的时候,不适合将文件的绝对路径显示出来,不太安全。

方案五:判断文件名是否相同 如果相同添加数字区别

场景:当前upload文件夹中,有10W文件,那么每做一次文件上传,都要进行10W次循环和判断,这样非常消耗系统性能,这个方案理论成立,实际执行,有问题

方案六:mac加时间

Mac:所有电子设备的物理地址,理论上,每个设备都是唯一的,但是,会出现修改地址的情况

注意:修改文件名的权限,应该,保存在服务器中,不应该将权限放给用户。

用户的唯一性识别:保证使用当前手机的是一个真实的人。保证mac地址是可以获取的(保证非法用户无法通过购买大两手机号进行注册),也不行,安卓或者ios的模拟器

方案七:毫秒值

双十一:一秒钟处理订单量,上百万的,同理,如果出现极端情况,在同一毫秒也会出现同名文件上传,那么也会出现问题。

注意:在考虑制作系统功能的时候,有时,需要考虑极端情况。

方案八:hashcode

同名的文件,他们的文件名计算出来的hashcode,应该是同样的,方案不可取。

方案九:随机数

比如:十个随机数,执行11次,就可能出现重复,所以不可取。

方案十:上传检查,如果重复,则返回,让用户重命名

用户操作复杂,不可取。

文件上传,先检查文件名,如果重复,让用户重写书写文件(注意:这个时候,让用户重命名,一定要给出明确的提示)。

例如:注册游戏,张三已经被注册,提示用户如果你要注册一个张三,(你可以用户名变成张三2333),

方案十一:不作判断,所有文件都加

每个文件上传后,获取文件名,然后直接添加一串数字(每次文件上传完成之后,数字加一)

嘿嘿嘿1.txt

嘿嘿嘿2.txt

嘿嘿嘿3.txt

BigInteger

需要数据的字段长度,可变(动态改变)

注意:在数据存储的时候,要注意当前数据不能超过,数据库字段的长度限制

注意:每次文件上传完成之后,数字加一,需要服务器之间数据同步,不然,肯定会出现重复

重名问题解决方案

UUID.randomUUID()这个方法就会生成一个唯一的字符串标识,不会重复。可以作为我们文件名使用。

代码演示:

4.3、目录分离

目录分离需求:

因为用户上传的文件,日积月累,文件量太大,导致打开一个文件夹的时候,需要时间太长

所以,我们需要将文件存放在不同的文件夹中。

场景:比如一个文件夹中,有1个文件,这时打开这个文件夹,需要一秒钟

     比如一个文件夹中,有10W个文件,这时打开这个文件夹,需要10W秒钟

     将10w文件存入1000个文件夹中,打开文件夹的速度,变快了

4.4、目录分离方案解决

1)每一个用户有一个专属的文件夹

2)每一个用户一个专属文件夹的基础上,每天生成一个文件夹,用来存当天上传的文件

    先是有一个张三专属的文件夹,然后,每天创建一个当前的文件夹,专门用来存储,当天上传的文件。

    方案二:由于现在网速的提升,特别是百兆千兆光纤进入普通用户,现在一天上传的文件量,非常大。

3)使用文件名的哈希值,来计算当前文件应该存储的位置

 

生成目录的工具类:

package cn.itcast.utils;

public class DirUtils {

 

    //根据文件名的哈希值,计算文件该存储的位置

    public static String getDir(String fileName){

        //获取哈希值

        int hashCode = fileName.hashCode();

        //开始计算

        //int类型数据,4个字节,32位,每四位获取一个数字,可以获取8个数字

        //8个数字,都,作为当前文件要存贮的目录

        //获取当前哈希值最低4位,作为一级目录

        int dir1 = hashCode & 15;

        //将哈希值,向右移动4位,更新最低四位

        hashCode = hashCode >> 4;

        //获取第二级目录

        int dir2 = hashCode & 15;

        

        return "/"+dir1+"/"+dir2;

    }

}

文件上传servlet修改:

//修改被上传文件应该储存的位置

//c:tomcat服务器webappsitcast-upload\upload12

realPath = realPath + DirUtils.getDir(fileName);

//在文件上传之前,先判断当前目录是否存在

File file = new File(realPath);

if(file.exists()){

//什么事情都不用干

}else{

//不存在,创建目录

    file.mkdirs();

}

5、下载介绍

5.1、超链接下载方式

    文件的下载有2种方式可以完成:

1、超链接形式:

<a href="资源的路径" >资源名称</a>

问题:

图片和文本资源会直接被解析到浏览器中,不是文件下载。

中文的文件名点击之后,是404,找不到,中文的文件名被url编码过了导致访问资源路径出错。

 

        2、使用Servlet通知浏览器下载:

            1)浏览器发送请求给服务器(用户要下载的文件名)

            2)服务器加载用户要下载的文件数据。

3)通知浏览器以下载的方式请求资源

4)使用IO技术,将数据发送(使用response对象发送数据)

    

    小结:

  1. 先修改页面——发送要下载的文件的文件名

  1. 定位要被下载的文件的位置(获取文件名的时候,处理中文乱码问题)
  2. 设置文件的媒体格式
  3. 解决中文文件名问题(先要判断浏览器,根据不同的浏览器,不同编码处理)
  4. 设置要被下载的文件名
  5. 使用IO技术将数据发送给浏览器(response获取的out输出流,不用手动关闭,自己使用的输入流,需要手动关闭)

5.2、服务器端写程序下载(重点:必须掌握)

package cn.itcast.servlet;

 

import java.io.File;

import java.io.FileInputStream;

import java.io.IOException;

import java.net.URLEncoder;

 

import javax.servlet.ServletException;

import javax.servlet.ServletOutputStream;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

 

import sun.misc.BASE64Encoder;

 

public class DownloadServlet extends HttpServlet {

 

    public void doGet(HttpServletRequest request, HttpServletResponse response)

            throws ServletException, IOException {

 

        //服务器加载用户要下载的文件数据。

        String realPath = this.getServletContext().getRealPath("/upload");

        //获取当前用户需要下载的文件的文件名

        String parameter = request.getParameter("fileName");

        //处理文件名的中文问题

        String fileName = new String(parameter.getBytes("iso-8859-1"),"utf-8");

        //加载数据

        FileInputStream in = new FileInputStream(new File(realPath,fileName));

        //设置文件的媒体格式

        response.setContentType(getServletContext().getMimeType(fileName));

        //中文的文件名在发送给浏览器之前,需要编码

        //解决编码之前,需要判断浏览器

        String header = request.getHeader("User-Agent");

        if(header.contains("Firefox")){

            //当前浏览器是火狐浏览器,使用base64编码

            BASE64Encoder base64Encoder = new BASE64Encoder();

            fileName = "=?utf-8?B?"    + base64Encoder.encode(fileName.getBytes("utf-8")) + "?=";

            

        }else{

            //其他浏览器,使用URLEncoder编码

            fileName = URLEncoder.encode(fileName,"utf-8");

        }

        //设置要被下载的文件名

        response.setHeader("Content-Disposition", "attachment;filename=" + fileName);

        //使用IO技术将数据发送给浏览器

        //使用response向浏览器发送响应

        ServletOutputStream out = response.getOutputStream();

        

        byte[] buf = new byte[1024];

        int len = 0;

        while((len = in.read(buf)) != -1){

            

            out.write(buf, 0, len);

        }

        in.close();

        

    }

 

    public void doPost(HttpServletRequest request, HttpServletResponse response)

            throws ServletException, IOException {

        doGet(request, response);

    }

 

}

响应消息头设置:

Content-Type 设置文件媒体格式

response.setContentType(getServletContext().getMimeType(filename));

 

Content-Disposition 设置要被下载的文件名

response.setHeader("Content-Disposition", "attachment;filename=" + filename);

        

谷歌浏览器(和主流其他浏览器)中文乱码处理:

        fileName = URLEncoder.encode(fileName,"utf-8");

火狐下载文件名乱码处理:(先判断是否是火狐浏览器)

BASE64Encoder base64Encoder = new BASE64Encoder();

filename = "=?utf-8?B?"    + base64Encoder.encode(filename.getBytes("utf-8")) + "?=";

 

        注意:如果无法使用BASE64Encoder这个类(无法导入这个包),按一下方式解决。

解决方法:按照如下方法设置Eclipse导入%JAVA_HOME%jrelib目录下的rt.jar包即可,Project->Properties,选择Java Build Path设置项,再选择Libraries标签,Add External Jars添加%JAVA_HOME%jrelib t.jar就可以使用

如果还是不行,可以再修改项目的JDK,将JDK版本改成本地安装的JDK版本

        

如果没有自己的JDK:

6.作业:

1)使用fileupload完成文件上传功能(40点积分)

2)使用js实现多文件上传功能(20点积分)

3)解决文件重名问题(10点积分)

4)解决文件目录分离问题(10点积分)

5)完成文件下载功能(10点积分)

6)解决文件下载中文乱码问题(10点积分)

原文地址:https://www.cnblogs.com/beyondcj/p/6270902.html