Spring第三讲

三、文件的上传和下载

1 文件上传

文件上传要求form表单的请求方式必须为post,并且添加属性enctype="multipart/form-data" SpringMVC中将上传的文件封装到MultipartFile对象中,通过此对象可以获取文件相关信息 上传步骤:

1.1 添加依赖

    <!--用于文件上传:-->
    <dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.4</version>
    </dependency>
​

1.2 在SpringMVC的配置文件中添加配置:

<!--    文件上传需要用到的bean,这里的代码不需要修改-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>

1.3 控制器方法

共用的前端页面:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>文件上传</title>
</head>
<body>
<%--
        文件上传的要求:
            1、method必须是post请求
            2、form表单属性中的enctype="multipart/form-data"
--%>
<form method="post" enctype="multipart/form-data" action="uploadFile3">
<%--    这里的name可以随便取值,但是在映射的方法填写形参时,值要和这里的name值相同 --%>
        请选择要上传的文件:<input type="file" name="file"><br>
        <input type="submit" value="提交" >
</form>
</body>
</html>

方法一:使用io流

@Controller
public class UploadController {
//      这里的形参值要和视图文件中的input type="file"标签中name的属性值相同
//        <input type="file" name="file"><br>
    @PostMapping("uploadFile")
    public String uploadFile(MultipartFile file, HttpServletRequest request){
//        获取文件的旧(原始)名字
        String oldname = file.getOriginalFilename();
//        输出文件原始的名字
        System.out.println("file = " + file.getOriginalFilename());
        // HttpServletRequest request用于获取当前项目上下文路径中的一个upload的文件夹
        String realPath = request.getServletContext().getRealPath("/upload");
        System.out.println("realPath = " + realPath);
//        先将该路径转化为一个file类,并判断该类是否存在,如果不存在就创建这个路径
        File file1 = new File(realPath);
        if (!file1.exists()){
            file1.mkdirs();
        }
//        取一个新的名字
        String s = UUID.randomUUID().toString();
//        获取原来原始名字的后缀
        int i = oldname.lastIndexOf(".");
        String suffixName = oldname.substring(i);
        System.out.println("原始文件名字的后缀suffixName = " + suffixName);
//        随机数+原始名字后缀获得一个不会重复的新名字
        String newname = s + suffixName;
​
//        将图片上传到刚才的文件夹中
        try {
//            输出流:用于写入文件
//            File.separator就是代表文件的分隔符,就相当于是\
            OutputStream outputStream = new FileOutputStream(file1+File.separator+newname);
//            当前的文件识别为输入流
            InputStream inputStream = file.getInputStream();
//          将输入流中的东西写入到输出流中
            int length = 0;
            while ((length=inputStream.read())!=-1){
                outputStream.write(length);
            }
            outputStream.flush();
            outputStream.close();
            inputStream.close();
            //        文件上传之后只需要将路径保存到数据库即可
            String picturePath = realPath+File.separator+newname;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "index";
    }

方法二: 使用file.transferto

@Controller
public class UploadController {
//      这里的形参值要和视图文件中的input type="file"标签中name的属性值相同
//        <input type="file" name="file"><br>
    @PostMapping("uploadFile2")
    public String uploadFile2(MultipartFile file, HttpServletRequest request){
//        获取文件的旧(原始)名字
        String oldname = file.getOriginalFilename();
//        输出文件原始的名字
        System.out.println("file = " + file.getOriginalFilename());
        // HttpServletRequest request用于获取当前项目上下文路径中的一个upload的文件夹
        String realPath = request.getServletContext().getRealPath("/upload");
        System.out.println("realPath = " + realPath);
//        先将该路径转化为一个file类,并判断该类是否存在,如果不存在就创建这个路径
        File file1 = new File(realPath);
        if (!file1.exists()){
            file1.mkdirs();
        }
//        取一个新的名字
        String s = UUID.randomUUID().toString();
//        获取原来原始名字的后缀
        int i = oldname.lastIndexOf(".");
        String suffixName = oldname.substring(i);
        System.out.println("原始文件名字的后缀suffixName = " + suffixName);
//        随机数+原始名字后缀获得一个不会重复的新名字
        String newname = s + suffixName;
//        将图片上传到刚才的文件夹中
        File file2 = new File(realPath+File.separator+newname);
//      将刚要上传的文件上传到这个路径里面
        try {
            file.transferTo(file2);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "index";
    }
}    

方法3:阿里云文件上传

前两种方法,只能将文件上传到文件路径中,但是假如删掉项目的target,那么上传的文件也会丢失,这样的数据并不安全,所以我们可以将文件上传到云端,这里选择的是阿里云文件

开通阿里云的对象存储功能

1)搜索阿里云 https://www.aliyun.com/

2)登录后找到对象存储进行开通,这里有免费的使用空间;

3)进入到oss控制台,点击创建Bucket

记下Bucket名称和Endpoint

读写权限可以设置为公共读写

4)点击创建一个新的AccessSecret,

记下AccessKey ID 和 AccessKey Secret

5)添加阿里云文件上传的依赖

官网入门文档-->SDK示例-->Java-->安装

    <!--    阿里云 oss存储文件,如果java版本在9以上,还需要添加别的依赖-->
    <dependency>
      <groupId>com.aliyun.oss</groupId>
      <artifactId>aliyun-sdk-oss</artifactId>
      <version>3.15.0</version>
    </dependency>
  </dependencies>

6)编写代码

阿里云官网有示例代码:

然后选择SDK示例-->Java-->上传文件-->简单上传

编写好的代码如下:

@Controller
public class UploadController {
    @PostMapping("uploadFile3")
    public String uploadFile3(MultipartFile file, HttpServletRequest request){
// Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
//        String endpoint = "https://oss-cn-shanghai.aliyuncs.com";
//        这里不加这个https也行
        String endpoint = "oss-cn-shanghai.aliyuncs.com";
        // 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
        String accessKeyId = "这里是我的accessKeyId ";
        String accessKeySecret = "这里是我的secret";
        // 填写Bucket名称,例如examplebucket。
        String bucketName = "这里是我的bucketName ";
        // 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。
        String oldname = file.getOriginalFilename();
​
        //        取一个新的名字
        String s = UUID.randomUUID().toString();
//        获取原来原始名字的后缀
        int i = oldname.lastIndexOf(".");
        String suffixName = oldname.substring(i);
        System.out.println("原始文件名字的后缀suffixName = " + suffixName);
//        随机数+原始名字后缀获得一个不会重复的新名字
        String newname = s + suffixName;
​
        String objectName = newname;
​
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
​
        try {
            // 填写字符串。
            String content = "Hello OSS";
​
            // 创建PutObjectRequest对象。
            //这里的第三个参数就是要上传的文件放入输入流,file就是当前要上传的文件名。
            PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName, file.getInputStream());
​
            // 如果需要上传时设置存储类型和访问权限,请参考以下示例代码。
            // ObjectMetadata metadata = new ObjectMetadata();
            // metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());
            // metadata.setObjectAcl(CannedAccessControlList.Private);
            // putObjectRequest.setMetadata(metadata);
​
            // 上传字符串。
            ossClient.putObject(putObjectRequest);
            String picturePath = "https://"+bucketName+"."+endpoint+"/"+oldname;
            System.out.println("文件的地址是:picturePath = " + picturePath);
//          图片地址  https://leiwenlin.oss-cn-shanghai.aliyuncs.com/%E4%BA%8C%E5%AF%B8%E7%99%BD%E5%BA%95.jpg
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        return "index";
    }
}

方法4:layui界面做文件上传

文件上传的时候,需要有<input typr="file",name="file"/>但是这里没有写明,
其实源码中他的input就是这么写的,所以在方法中接收时,MultipartFile的别名就是file
​
另外注意:layui中发出的请求所接受的返回值都应该是一个LayData类型的数据,也就是code,msg,data类型的数据
    这里跳转页面后,最后需要页面返回的数据中要有一个code值,否则话文件可以上传成功,但是会提示上传接口异常

前端页面:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
<%--
    ${pageContext.request.contextPath}这是获取上下文的路径,相当于是获取了当前的项目名
    这里要引入css样式,也要引入js的路径
--%>
    <link rel="stylesheet" href="${pageContext.request.contextPath}/static/layui-v2.7.6/layui/css/layui.css">
<%--    在下面的代码中也可以引入js代码--%>
<%--    <script type="application/javascript" src="${pageContext.request.contextPath}/static/layui-v2.7.6/layui/layui.js"></script>--%>
</head>
<body>
<div class="layui-upload">
<%--
        文件上传的时候,需要有<input typr="file",name="file"/>
        但是这里没有写明,其实源码中他的input就是这么写的,所以在方法中接收时,MultipartFile的别名就是file
--%>
    <button type="button" class="layui-btn" id="test1">上传图片</button>
    <div class="layui-upload-list">
        <img class="layui-upload-img" id="demo1" style="height: 100px;width: 100px;border-radius: 50%">
        <p id="demoText"></p>
    </div>
    <div style="width: 95px;">
        <div class="layui-progress layui-progress-big" lay-showpercent="yes" lay-filter="demo">
            <div class="layui-progress-bar" lay-percent=""></div>
        </div>
    </div>
</div>
<a name="list-progress"> </a>
<%--引入js代码--%>
<script type="application/javascript" src="${pageContext.request.contextPath}/static/layui-v2.7.6/layui/layui.js"></script>
<script>
<%--
    另外注意:layui中发出的请求所接受的返回值都应该是一个LayData类型的数据,也就是code,msg,data类型的数据
    这里跳转页面后,最后需要页面返回的数据中要有一个code值,否则话文件可以上传成功,但是会提示上传接口异常
--%>
    layui.use(['upload', 'element', 'layer'], function() {
        var $ = layui.jquery
            , upload = layui.upload
            , element = layui.element
            , layer = layui.layer;
        //常规使用 - 普通图片上传
        var uploadInst = upload.render({
            elem: '#test1'
            , url: '${pageContext.request.contextPath}/uploadFile4' //此处用的是第三方的 http 请求演示,实际使用时改成您自己的上传接口即可。
            , before: function (obj) {
                //预读本地文件示例,不支持ie8
                obj.preview(function (index, file, result) {
                    $('#demo1').attr('src', result); //图片链接(base64)
                });
                element.progress('demo', '0%'); //进度条复位
                layer.msg('上传中', {icon: 16, time: 0});
            }
            , done: function (res) {
                //如果上传失败
                if (res.code > 0) {
                    return layer.msg('上传失败');
                }
                //上传成功的一些操作
                //……
                $('#demoText').html(''); //置空上传失败的状态
            }
            , error: function () {
                //演示失败状态,并实现重传
                var demoText = $('#demoText');
                demoText.html('<span style="color: #FF5722;">上传失败</span> <a class="layui-btn layui-btn-xs demo-reload">重试</a>');
                demoText.find('.demo-reload').on('click', function () {
                    uploadInst.upload();
                });
            }
            //进度条
            , progress: function (n, elem, e) {
                element.progress('demo', n + '%'); //可配合 layui 进度条元素使用
                if (n == 100) {
                    layer.msg('上传完毕', {icon: 1});
                }
            }
        });
    });
</script>
</body>
</html>

不变的代码可以设置为常量:

public class AliyunUtil {
    public static final String END_POINT = "oss-cn-shanghai.aliyuncs.com";
    public static final String  ACCESS_KEY_ID = "your accessKeyId";
    public static final String  ACCESS_KEY_SECRET = "your accessKeySecret";
    public static final String  BUCKET_NAME = "your bucketName ";
}

后台代码:

@Controller
public class UploadController {
    @PostMapping("uploadFile4")
    public LayData uploadFile4(MultipartFile file, HttpServletRequest request){
// Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
//        String endpoint = "https://oss-cn-shanghai.aliyuncs.com";
//        这里不加这个https也行
        String endpoint = AliyunUtil.END_POINT;
        // 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
        String accessKeyId = AliyunUtil.ACCESS_KEY_ID;
        String accessKeySecret = AliyunUtil.ACCESS_KEY_SECRET;
        // 填写Bucket名称,例如examplebucket。
        String bucketName = AliyunUtil.BUCKET_NAME;
        // 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。
        String oldname = file.getOriginalFilename();
​
        //        取一个新的名字
        String s = UUID.randomUUID().toString();
//        获取原来原始名字的后缀
        int i = oldname.lastIndexOf(".");
        String suffixName = oldname.substring(i);
        System.out.println("原始文件名字的后缀suffixName = " + suffixName);
//        随机数+原始名字后缀获得一个不会重复的新名字
        String newname = s + suffixName;
​
        String objectName = newname;
​
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
​
        try {
            // 创建PutObjectRequest对象。
            PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName, file.getInputStream());
​
            // 上传字符串。
            ossClient.putObject(putObjectRequest);
​
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        String picturePath = "https://"+bucketName+"."+endpoint+"/"+oldname;
//        这里需要返回的是一个LayData类型的数据,否则layui接受的数据格式不对,会导致异常
        return new LayData(picturePath);
    }
}

2 文件下载

// 下载
@RequestMapping(value="/download",method= RequestMethod.GET) //匹配的是href中的download请求
public ResponseEntity<byte[]> download(HttpServletRequest request, @RequestParam("filename") String filename) throws IOException{
​
    String downloadFilePath=request.getServletContext().getRealPath("/upload");
​
    File file = new File(downloadFilePath+File.separator+filename);//新建一个文件
​
    HttpHeaders headers = new HttpHeaders();//http头信息
​
    String downloadFileName = new String(filename.getBytes("UTF-8"),"iso-8859-1");//设置编码
​
    headers.setContentDispositionFormData("attachment", downloadFileName);
​
    headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
​
    //MediaType:互联网媒介类型  contentType:具体请求中的媒体类型信息
​
    return new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(file),headers, HttpStatus.CREATED);
​
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值