SperingBoot+vue文件上传&下载&预览

上传文件:
前端:
  • 整个过程,就是在使用FormData 添加 上File(这个Blob),并且key要和后台的名字对应上
  • 在点击上传按钮开始上传之前,使用了URL.createObjectURL(File)创建blobUrl,给了img标签作图片预览
  • 上传完毕后,将input file的value置为空。若将input file置为空,则此时不能再从input file中获取file了,得等下次再选择图片才能获得file,将它置为空的目的是为了下次选择同样的图片,也能触发input file的change事件
后端:
  • 后台仅仅就是用MultipartFile声明接收即可,可以使用@RequestParam注解 或 @RequestPart注解
  • 调用MultipartFile#transferTo保存文件
  • 可以从MultipartFile#getInputStream中获取流,比如上传到OSS。
前端代码:
复制代码
<template>
    <div>
        选择文件: <input type="file" ref="fileInputRef" @change="selectFile" multiple> <!-- 使用multiple属性,可选择多个文件 -->
        <br/>
        <img v-if="imgUrl" :src="imgUrl" alt="" style="width:54px;height:54px;">
        <el-button v-if="imgUrl" type="primary" @click="uploadFile">上传</el-button>

        <hr/>
    </div>
</template>

<script>
import axiosInstance from '@/utils/request.js'
import axios from 'axios'
export default {
    name: 'File',
    data() {
        return {
            imgUrl:''
        }
    },
    methods: {
        selectFile() {

            let file = this.$refs['fileInputRef'].files[0]
            console.log(file)

            // 上传前, 可以预览该图片
            let blobUrl = URL.createObjectURL(file)
            this.imgUrl = blobUrl

        },
        uploadFile() {

            // 因为可能选择多个文件, 所以这里是个数组
            let file = this.$refs['fileInputRef'].files[0]

            let formData = new FormData()

            formData.append('mfile', file) // 必须和后端的参数名相同。(我们看到了, 其实就是把blob文件给了formData的一个key)
            formData.append("type", 'avatar')

            // 可以有下面2种方式, 来上传文件
            /* axiosInstance
                .post('http://127.0.0.1:8083/file/uploadFile',formData, {headers: {'a':'b'}})
                .then(res => {
                    console.log('响应回来: ',res);
                }) */
            axiosInstance({ // 这种传参方式, 在axios的index.d.ts中可以看到
                url:'http://127.0.0.1:8083/file/uploadFile',
                method:'post',
                data: formData, // 直接将FormData作为data传输
                headers: {
                    'a':'b' // 可携带自定义响应头
                }
            }).then(res => {
                console.log('响应回来: ',res);
            })

            console.log(this.$refs['fileInputRef'].value); // C:\fakepath\cfa86972-07a1-4527-8b8a-1991715ebbfe.png
            // 上传完文件后, 将value置为空, 以避免下次选择同样的图片而不会触发input file的change事件。
            // (注意清空value后,将不能再从input file中获取file,而原先的file仍然能够使用)
            this.$refs['fileInputRef'].value = ''
        }
    }
}
</script>

<style>

</style>
后端代码:
复制代码
@PostMapping("uploadFile")
public Object uploadFile(@RequestPart("mfile")MultipartFile multipartFile,@RequestPart("type") String type) throws IOException {

    System.out.println(multipartFile.getClass());
    System.out.println(type);

    // 源文件名
    String originalFilename = multipartFile.getOriginalFilename();
    // 内容类型
    String contentType = multipartFile.getContentType();
    // 文件是否为空(无内容)
    boolean empty = multipartFile.isEmpty();
    // 文件大小
    long size = multipartFile.getSize();
    // 文件的字节数据
    byte[] bytes = multipartFile.getBytes();
    // 获取文件的字节输入流
    InputStream inputStream = multipartFile.getInputStream();
    // 将文件保存到指定路径下
    multipartFile.transferTo(new File("d:/Projects/practice/test-springboot/src/main/resources/file/" + originalFilename));

    System.out.println(originalFilename);
    System.out.println(contentType);
    System.out.println(empty);
    System.out.println(size);
    System.out.println(bytes.length);

    HashMap<String, Object> data = new HashMap<>();
    data.put("data", "ok");
    return data;
}
动态a标签下载
前端代码
  • 只需要动态创建a标签,添加到body,然后手动调用js触发a标签的click事件,触发下载

  • 下载完成之后,将a标签移除

  • 整个过程a标签的样式都是display:none

后端代码:
复制代码
@GetMapping("downloadFile")
public void downloadFile(@RequestParam("filename") String filename) throws Exception {

    // 告知浏览器这是一个字节流,浏览器处理字节流的默认方式就是下载
    // 意思是未知的应用程序文件,浏览器一般不会自动执行或询问执行。浏览器会像对待,
    // 设置了HTTP头Content-Disposition值为attachment的文件一样来对待这类文件,即浏览器会触发下载行为
    response.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE);
    // ,该响应头指示回复的内容该以何种形式展示,是以内联的形式(即网页或者网页的一部分),还是以附件的形式下载并保存到本地。
    response.setHeader(HttpHeaders.CONTENT_DISPOSITION,"attachment;fileName="+ URLEncoder.encode(filename, "UTF-8"));
    File file = new File("d:/Projects/practice/test-springboot/src/main/resources/file/" + filename);

    ServletOutputStream ros = response.getOutputStream();

    FileInputStream fis = new FileInputStream(file);
    byte[] bytes = new byte[2 * 1024];
    int len = 0;
    while ((len = fis.read(bytes)) != -1) {
        ros.write(bytes, 0, len);
    }

    ros.flush();
    ros.close();
    fis.close()

}
预览文件:
前端代码:
复制代码
<template>
    <div>
        <a href="http://127.0.0.1:8083/file/previewFile?filename=头像a.png">头像a.png</a>
    </div>
</template>

<script>
import axios from 'axios'
export default {
    name: 'File',
    data() {
        return {
        }
    },
    methods: {
        
    }
}
</script>

<style>

</style>
后端代码:
复制代码
@GetMapping("previewFile")
    public void previewFile(@RequestParam("filename") String filename) throws Exception {

    // 可使用ServletContext 通过文件名获取 媒体资源类型
    response.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.IMAGE_PNG_VALUE);
    File file = new File("d:/Projects/practice/test-springboot/src/main/resources/file/" + filename);

    ServletOutputStream ros = response.getOutputStream();

    // 可参考: StreamUtils
    FileInputStream fis = new FileInputStream(file);
    byte[] bytes = new byte[4 * 1024];
    int len = 0;
    while ((len = fis.read(bytes)) != -1) {
        ros.write(bytes, 0, len);
    }

    ros.flush();
    ros.close();
	fis.close()
}
相关推荐
paopaokaka_luck8 分钟前
基于springboot3+vue3的车间生产管理系统(Echarts图形化分析、BI报表)
java·前端·spring boot·学习·echarts
程序员鱼皮14 分钟前
3 大 DeepSeek Harness 进阶玩法,招多个大肥鱼帮我干活!
前端·后端·ai编程
KoPa17 分钟前
HeySmart:大模型开源网关基座-请求生命周期与钩子引擎
前端·后端
悟空瞎说26 分钟前
# Dispatch(GCD)苹果官方文档 白话文讲解(Objective‑C 版本文档)
javascript
七牛开发者37 分钟前
Coding Agent 如何跑稳长任务?从上下文管理到运行时状态
前端·javascript·后端
半个落月1 小时前
从 CSR 到 Server Component:吃透 Next.js 16 App Router 路由、布局与 SEO
前端·next.js
七牛开发者1 小时前
拆解 DeepSeek Harness:Profile 与 Bundle 如何装配运行时
前端·javascript·后端
葡萄城技术团队1 小时前
表格智能体系列 · 1:一句话怎么变成一次表格操作
前端
梦曦i1 小时前
Vue-Router 2.4.0 新增可控重定向功能
前端·uni-app
deli0070071 小时前
Markdown 实时预览器:左边写右边看,排版效率翻倍
前端·ai编程