SpringBoot+Vue实现文件上传下载功能

news2025/1/22 17:49:43

前言

本文主要实现了以下功能:
1、 单文件上传以及多文件上传功能
2、 输入文件名下载文件功能
3、 输入音频文件名在线播放音频功能

一、项目基础部分搭建

1.1 前端项目搭建

1.1.1 新建前端项目

打开命令行输入以下命令,使用Vue CLI创建前端项目,Vue CLI安装教程

vue create file-demo

在这里插入图片描述

1.1.2 引入axios

输入以下命令在项目中引入axios

npm install axios --save

在这里插入图片描述

1.1.3 解决跨域问题

打开vue.config.js添加以下配置,修改启动端口,以及配置代理解决跨域问题

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true
})

module.exports = {
  devServer: {
    // 修改前端项目启动端口号
    port: 8081,
    proxy: {
      '^/file': {
        // 此处配置对应的后端端口
        target: "http://localhost:8080",
        // 如果是https接口,需要配置这个参数为true
        secure: false,
        // 此处配置路径重写
        pathRewrite: {
          '^/file': '/file'
        }
      }
    }
  }
}

1.2 后端项目搭建

1.2.1 新建后端项目

打开IDEA,按照以下步骤创建一个新的SpringBoot项目
在这里插入图片描述
在这里插入图片描述

1.2.2 编辑配置文件

打开项目,编辑application.properties配置文件,输入以下配置
在这里插入图片描述

#可以选择性的修改或选择以下配置
#配置服务端口
server.port=8080
#是否开启文件上传支持,默认是true
spring.servlet.multipart.enabled=true
#文件写入磁盘的阈值,默认是0
spring.servlet.multipart.file-size-threshold=0
#单个文件的最大值,默认是50MB
spring.servlet.multipart.max-file-size=50MB
#多个文件上传时的总大小 值,默认是100MB
spring.servlet.multipart.max-request-size=100MB
#是否延迟解析,默认是false
spring.servlet.multipart.resolve-lazily=false
#自定义文件访问路径
myfile.path=E:\\test\\dir

二、文件上传功能

2.1 单文件上传功能实现

2.1.1 前端代码

App.vue中添加如下代码,使用form标签实现文件上传功能

<template>
  <p>单文件上传</p>
  <form action="/file/uploadSingleFile" method="post" enctype="multipart/form-data">
    文件:
    <input type="file" name="file">
    <input type="submit">
  </form>
</template>

2.1.2 后端代码

com.example.springbootdemo.controller包下创建UploadFileController.java文件

package com.example.springbootdemo.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;

@Slf4j
@RestController
@RequestMapping("/file")
public class UploadFileController {

    @Value("${myfile.path}")
    private String filePath;

    // 单文件上传功能
    @PostMapping("/uploadSingleFile")
    public void uploadSingleFile(@RequestParam("file") MultipartFile multipartFile) {
        String fileName = multipartFile.getOriginalFilename();
        File file = new File(filePath + '\\' + fileName);
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
            log.info("父级文件目录不存在,已创建目录");
        }
        try {
            multipartFile.transferTo(file);
        } catch (IOException e) {
            log.error("{}",e);
            log.error("程序错误,请重新上传");
            e.printStackTrace();
        } finally {
            log.info("文件上传成功,文件全路径名称为:{}",file.getPath());
        }
    }
}

2.2 多文件上传功能实现

2.2.1 前端代码

App.vue中添加如下代码,使用form标签实现文件上传功能

<template>
  <p>多文件上传</p>
  <form action="/file/uploadMultipleFile" method="post" enctype="multipart/form-data">
    文件:
    <input type="file" name="files" multiple="multiple">
    <input type="submit">
  </form>
</template>

2.2.2 后端代码

com.example.springbootdemo.controller包下创建UploadFileController.java文件

package com.example.springbootdemo.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;

@Slf4j
@RestController
@RequestMapping("/file")
public class UploadFileController {

    @Value("${myfile.path}")
    private String filePath;

    // 多文件上传功能实现
    @PostMapping("/uploadMultipleFile")
    public void uploadMultipleFile(@RequestParam("files") MultipartFile multipartFiles[]) {
        for (MultipartFile multipartFile : multipartFiles) {
            String fileName = multipartFile.getOriginalFilename();
            File file = new File(filePath + '\\' + fileName);
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
                log.info("父级文件目录不存在,已创建目录");
            }
            try {
                multipartFile.transferTo(file);
            } catch (IOException e) {
                log.error("{}",e);
                log.error("程序错误,请重新上传");
                e.printStackTrace();
            } finally {
                log.info("文件上传成功,文件全路径名称为:{}",file.getPath());
            }
        }
    }
}

三、文件下载功能

3.1 普通文件下载功能实现

3.1.1 前端代码

App.vue中添加如下代码,使用form标签实现文件上传功能

<template>
  <p>文件下载{{inputData.fileName}}</p>
  <input type="text" placeholder="请输入全文件名" v-model="inputData.fileName">
  <button @click="downloadFile">下载</button>
</template>

<script>
import axios from 'axios';
import { reactive } from 'vue';

export default{
  setup() {
    let inputData = reactive({
      fileName:""
    })

    // 下载文件函数
    async function downloadFile() {
      let BASE_URL = "/file";
      let data = {
        ...inputData
      }
      console.log(inputData);
      await axios({
        url: `${BASE_URL}/downloadFile`,
        method: "post" ,
        data: data,
        headers: {
          'Content-Type': 'application/json'
        },
        responseType: 'blob',
      }).then((resp) => {
        const blob = new Blob([resp.data]);
        var downloadElement = document.createElement("a");
        var href = window.URL.createObjectURL(blob);
        downloadElement.href = href;
        downloadElement.download = decodeURIComponent(inputData.fileName);
        document.body.appendChild(downloadElement);
        downloadElement.click();
        document.body.removeChild(downloadElement);
        window.URL.revokeObjectURL(href);
      });
    }
    
    return {
      inputData,
      downloadFile
    }
  }
}
</script>

3.1.2 后端代码

com.example.springbootdemo.controller包下建立DownloadFileController

package com.example.springbootdemo.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.Map;

@Slf4j
@RestController
@RequestMapping("/file")
public class DownloadFileController {

    @Value("${myfile.path}")
    private String filePath;

    @PostMapping("/downloadFile")
    public void downloadFile(@RequestBody Map<String, String> params, HttpServletRequest request, HttpServletResponse response) {
        log.info("文件名为:{}",params.get("fileName"));
        if (!params.containsKey("fileName") || params.get("fileName") == null || "".equals(params.get("fileName"))) {
            log.info("文件名不存在");
            return;
        }
        if (filePath == null || "".equals(filePath)) {
            log.info("文件路径不存在");
            return;
        }
        String fileName = params.get("fileName");
        String fullPath = filePath + "\\" + fileName;
        try {
            download(request,response, fullPath, fileName);
        } catch (Exception e) {
            log.error("{}",e);
            log.error("文件下载失败");
            e.printStackTrace();
        }
    }

    // 下载文件方法:
    public static void download(HttpServletRequest request, HttpServletResponse response, String filePath, String realName) throws Exception {
        response.setContentType("text/html;charset=UTF-8");
        request.setCharacterEncoding("UTF-8");
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        long fileLength = (new File(filePath)).length();
        response.setContentType("application/octet-stream;charset=GBK");
        response.setHeader("Content-disposition", "attachment; filename=" + new String(realName.getBytes("GB2312"), "ISO-8859-1"));
        response.setHeader("Content-Length", String.valueOf(fileLength));
        bis = new BufferedInputStream(new FileInputStream(filePath));
        bos = new BufferedOutputStream(response.getOutputStream());
        byte[] buff = new byte[2048];

        int bytesRead;
        while(-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
            bos.write(buff, 0, bytesRead);
        }

        bis.close();
        bos.close();
    }
}

3.2 音频文件在线播放功能实现

3.2.1 前端代码

App.vue中添加如下代码,使用form标签实现文件上传功能

<template>
  <p>文件下载{{inputData.fileName}}</p>
  <input type="text" placeholder="请输入全文件名" v-model="inputData.fileName">
  <button @click="downloadFile">下载</button>

  <p>音乐在线播放{{}}</p>
  <input type="text" placeholder="请输入音乐文件名" v-model="inputData.fileName">
  <button @click="playMusic">播放音乐</button>
  <br>
  <audio controls currentTime autoplay :src='audioSrc.data'></audio>
</template>

<script>
import axios from 'axios';
import { reactive } from 'vue';

export default{
  setup() {
    let inputData = reactive({
      fileName:""
    })
    let audioSrc = reactive({
      data:""
    });
    
    // 在线播放音乐函数
    async function playMusic() {
      let BASE_URL = "/file";
      let data = {
        ...inputData
      }
      console.log(inputData);
      await axios({
        url: `${BASE_URL}/downloadFile`,
        method: "post" ,
        data: data,
        headers: {
          'Content-Type': 'application/json'
        },
        responseType: 'blob',
      }).then((Blobdata) => {
        audioSrc.data = window.URL.createObjectURL(Blobdata.data);
      });
    }
    return {
      inputData,
      audioSrc,
      playMusic
    }
  }
}
</script>

3.2.2 后端代码

com.example.springbootdemo.controller包下建立DownloadFileController

package com.example.springbootdemo.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.Map;

@Slf4j
@RestController
@RequestMapping("/file")
public class DownloadFileController {

    @Value("${myfile.path}")
    private String filePath;

    @PostMapping("/downloadFile")
    public void downloadFile(@RequestBody Map<String, String> params, HttpServletRequest request, HttpServletResponse response) {
        log.info("文件名为:{}",params.get("fileName"));
        if (!params.containsKey("fileName") || params.get("fileName") == null || "".equals(params.get("fileName"))) {
            log.info("文件名不存在");
            return;
        }
        if (filePath == null || "".equals(filePath)) {
            log.info("文件路径不存在");
            return;
        }
        String fileName = params.get("fileName");
        String fullPath = filePath + "\\" + fileName;
        try {
            download(request,response, fullPath, fileName);
        } catch (Exception e) {
            log.error("{}",e);
            log.error("文件下载失败");
            e.printStackTrace();
        }
    }

    // 下载文件方法:
    public static void download(HttpServletRequest request, HttpServletResponse response, String filePath, String realName) throws Exception {
        response.setContentType("text/html;charset=UTF-8");
        request.setCharacterEncoding("UTF-8");
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        long fileLength = (new File(filePath)).length();
        response.setContentType("application/octet-stream;charset=GBK");
        response.setHeader("Content-disposition", "attachment; filename=" + new String(realName.getBytes("GB2312"), "ISO-8859-1"));
        response.setHeader("Content-Length", String.valueOf(fileLength));
        bis = new BufferedInputStream(new FileInputStream(filePath));
        bos = new BufferedOutputStream(response.getOutputStream());
        byte[] buff = new byte[2048];

        int bytesRead;
        while(-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
            bos.write(buff, 0, bytesRead);
        }

        bis.close();
        bos.close();
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/408376.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

浏览器同源策略导致跨域问题 No ‘Access-Control-Allow-Origin‘ header 原因及解决方式--(后端、nginx、前端)

目录 现象 原因 浏览器同源策略 导致结果&#xff1a; 解决方案 跨源资源共享&#xff08;CORS&#xff09; 各个端解决方法&#xff1a; 后端&#xff1a; 方式1&#xff1a;重载WebMvcConfigurer方法 方式2&#xff1a;配置监听CorsFilter 方式3&#xff1a;相关类…

微信小程序--》你真的了解小程序组件的使用吗?

&#x1f3cd;️作者简介&#xff1a;大家好&#xff0c;我是亦世凡华、渴望知识储备自己的一名在校大学生 &#x1f6f5;个人主页&#xff1a;亦世凡华、 &#x1f6fa;系列专栏&#xff1a;微信小程序 &#x1f6b2;座右铭&#xff1a;人生亦可燃烧&#xff0c;亦可腐败&…

JAVA-分页查询

分页查询 分页查询将数据库中庞大的数据分段显示,每页显示用户自定义的行数,提高用户体验度,最主要的是如果一次性从服务器磁盘中读出全部数据到内存,有内存溢出的风险 真假分页 假分页: 其原理还是将所有的数据读到内存中,翻页从内存中读取数据, 优点: 实现简单,性能高 缺点…

HTML+CSS+JS网页设计期末课程大作业 京剧文化水墨风书画

Web前端开发技术 描述 网页设计题材&#xff0c;DIVCSS 布局制作,HTMLCSS网页设计期末课程大作业&#xff0c;茶文化网站 | 中华传统文化题材 | 京剧文化水墨风书画 | 中国民间年画文化艺术网站 | HTML期末大学生网页设计作业 HTML&#xff1a;结构 CSS&#xff1a;样式 在操作…

EasyExcel复杂表头导出(一对多)升级版

一、前言 在之前写的 EasyExcel复杂表头导出&#xff08;一对多&#xff09;的博客的结尾&#xff0c;受限于当时的能力和精力&#xff0c;留下一些问题及展望。现在写下此博客&#xff0c;目的就是解决之前遗留的问题。 背景介绍&#xff0c;见上述链接指向的博客&#xff0c;…

vue3 route和router的区别以及如何传参数接受参数,如何像vue2一样使用$route和$router详解

vue3 route和router的区别以及如何传参数接受参数&#xff0c;如何像vue2一样使用$route和$router详解 因为我们在 setup 里面没有访问 this&#xff0c;所以我们不能再直接访问 this.$router 或 this.$route。作为替代&#xff0c;我们使用 useRouter 和useRoute函数,或者 Vu…

HTML小游戏21 —— html5版暴打皮卡丘游戏(附完整源码)

&#x1f482; 网站推荐:【神级源码资源网】【摸鱼小游戏】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 想寻找共同学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】本节教程我会带大家使用 HTML…

微信小程序--基础内容(详解)(一)

一、常用标签 1、view 标签 view 标签是一个块级元素&#xff0c;类似于 div&#xff08;小程序里面没有div标签&#xff09;&#xff0c;里面可以放任何内容或者插值表达式&#xff0c;如下所示&#xff1a; <view>这是view标签<view> <view>{{num}}<…

谷歌新版本跨域错误深度剖析与解决:request client is not a secure context and the resource is in more-private address

快速解决&#xff1a; 最近在测试http服务时&#xff0c;谷歌浏览器报了以下错误 “The request client is not a secure context and the resource is in more-private address space ‘local’”. 从报错信息来看&#xff0c;“不安全的请求方请求了更私有的本地资源” 对于…

API接口开发其实特简单,Python Flask Web 框架教程来了

大家好&#xff0c;日常工作中&#xff0c;无论你是数据工程师、数据挖掘工程师&#xff0c;甚至数据分析人员&#xff0c;都不可避免的与他人进行数据交互&#xff0c;API接口提供数据是最常见的形式。 今天我给大家分享 Python Flask Web 框架教程&#xff0c;共计10个部分&…

Vue面试题你知道多少

✅作者简介&#xff1a;大家好我是hacker707,大家可以叫我hacker&#xff0c;新星计划第三季python赛道Top1&#x1f3c6; &#x1f4c3;个人主页&#xff1a;hacker707的csdn博客 &#x1f525;系列专栏&#xff1a;带你玩转Vue &#x1f4ac;推荐一款模拟面试、刷题神器&…

前端401错误 解决方法:响应拦截器

目录 1.该问题出现的原因 2.处理401问题的解决方案原理 3.使用响应拦截器解决问题 1.该问题出现的原因 在前后端分离项目中&#xff0c;最常见的是前端点击登录后&#xff0c;后端返回token字符串&#xff0c;这个token可以看作是一个“令牌”&#xff0c;就比如你去酒店办理…

Vue实战【调整Vue-element-admin中的菜单栏,并添加顶部模块菜单栏】

目录&#x1f31f;前言&#x1f31f;小伙伴们先看&#x1f31f;实现思路&#x1f31f;具体代码&#x1f31f;最后&#x1f31f;前言 因为最近在整合公司的项目&#xff0c;需要把所有系统里的功能集成到一个项目里&#xff0c;这样就导致菜单栏目录会特别的多&#xff0c;不便…

【JavaScript】手撕前端面试题:事件委托 | 判断URL是否合法 | 全排列

&#x1f5a5;️ NodeJS专栏&#xff1a;Node.js从入门到精通 &#x1f5a5;️ 博主的前端之路&#xff08;源创征文一等奖作品&#xff09;&#xff1a;前端之行&#xff0c;任重道远&#xff08;来自大三学长的万字自述&#xff09; &#x1f5a5;️ TypeScript知识总结&…

【Axure】Axure RP 9下载、安装、授权、汉化

目录一、Axure RP 9 下载二、Axure RP 9 安装三、Axure RP 9 授权四、Axure RP 9 汉化一、Axure RP 9 下载 1、Axure RP 9 下载地址&#xff1a;https://www.axure.com/release-history/rp9 2、其他版本下载地址 ①登录axure官网:https://www.axure.com/ ②拉到最下面找到相关…

很好看的爱心表白代码(动态)

分享几个好看的爱心表白代码❤️爱心代码❤️&#xff08;C语言&#xff09;❤️流动爱心❤️&#xff08;htmlcssjs&#xff09;❤️线条爱心❤️&#xff08;htmlcssjs&#xff09;❤️biu表白爱心❤️&#xff08;htmlcssjs&#xff09;❤️matlab爱心函数❤️&#xff08;需…

Vue3+TS+Vite 入门指南

最近尝试上手 Vue3TSVite&#xff0c;对比起 Vue2 有些不适应&#xff0c;但还是真香~ 上手前先说下 Vue3 的一些变化吧~ Vue3 的变化 Vue3 带来的变化主要有以下几个方面&#xff1a; 使用层面 对比起 Vue2 启动速度快很多&#xff0c;新项目从 1s 升级到不到 500msvite.co…

Element-UI新手学习记录(一)

Layout 布局 通过基础的 24 分栏&#xff0c;迅速简便地创建布局。 span的作用 一行默认24个span&#xff0c;属性放在el-col中决定此元素占据多少span gutter属性 放在el-row中&#xff0c;给各个块之前设置间隔&#xff0c;但是是割的代码块的宽度。 offset属性 放在el…

小程序页面之间数据传递的四种方法

近期再使用小程序开发的时候遇到小程序页面和页面之间的数据传递问题。总结一下大致有以下几种方式实现页面数据传递。 最常见的就是路由传参&#xff0c;使用场景主要是页面汇总的少量数据的传递。以下都以Tarovue示例&#xff0c;原生、react或者uniapp同理&#xff0c;替换…

Pinia(二)了解和使用Store

Store Store 是保存状态(state)和业务逻辑的实体, store 不应该与我们的组件绑定. 换句话说, store 就是全局状态.store 有三个关键概念, 分别是 state, getters 和 actions, 这与 Vue 组件中的 data, computed 和 methods 是相对应的概念. 定义 store 通过 defineStore 函数…