最近在研究SpringBoot+Vue的文件上传,踩了不少坑。现在将正确的文件上传流程分享一下。

一、前端采用ElementUI组件

前端页面完整代码:

<template>
  <el-upload
    class="upload-demo"
    action="http://localhost:1111/01/fileup"
    :on-preview="handlePreview"  //钩子的值是方法名,即钩子被触发后要执行的函数
    :on-remove="handleRemove"
    :before-remove="beforeRemove"
    multiple
    :limit="3"
    :on-change="handChange"
    :on-exceed="handleExceed"
    :auto-upload="false"
    //上传的文件保存在fileList中
    :file-list="fileList">
    <el-button size="small" type="primary">点击上传</el-button>
    <div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div>
    <el-button size="small" @click="fileup">提交</el-button>
  </el-upload>
</template>

<script>
    export default {
        name: "fileup",
        data() {
            return {
                currentFile:[]    //作为存储中介,保存当前fileList已经添加的文件,用于axios发送文件
            }
        },
        methods: {
            handleRemove(file, fileList) {
                console.log(file, fileList);
            },
            handlePreview(file) {
                console.log(file);
            },
            handChange(file,fileList){       
            	//一旦选中文件,就将该文件添加到currentFile中
                this.currentFile.push(file)
            },
            handleExceed(files, fileList) {
                this.$message.warning(`当前限制选择 3 个文件,本次选择了 ${files.length} 个文件,共选择了 ${files.length + fileList.length} 个文件`);
            },
            beforeRemove(file, fileList) {
                return this.$confirm(`您确定要移除 ${ file.name }吗?`);
            },
            fileup(){
            	//必须使用FormData对象向后台传递文件
                let formData=new FormData();
                let key;
                //将待提交的文件放到FormData对象中,必须挨个放
                for (key in this.currentFile){
                    formData.append("currentFile",this.currentFile[key].raw)
                }
                this.axios({
                    method:'post',
                    data:formData,
                    url:'http://localhost:1111/01/fileup'
                }).then(function(resp){
                    console.log(resp.data);
                })
            }
        }
    }
</script>

<style scoped>
</style>

1、< el-upload>组件属性的解释

(1)action:这个属性是上传文件的地址。当我们指定了 auto-upload 属性,组件就会自动按 action 的地址提交。如果设置这个属性action=“none”,可以进行自定义,拓展性强。
(2)auto-upload:自动上传(选中文件立即上传),默认值是true,最好是设置为false,这样可以在点击提交按钮再提交。
(3)limit: 限制了上传文件的个数 。
(4)multiple:这个属性支持多文件上传,如果你是上传单文件这个属性设不设置都可以,如果是多文件,就要设置。
(5)accept:限制选择文件的类型。
(6)on-preview:当你选择文件后会显示已选择的文件列表,当你点击那个文件列表的的文件,就会触发钩子执行函数。
(7)before-upload:当你上传文件之前触发的函数(注意是上传文件前而不是选择文件之前)。
(8)on-remove:文件列表移除文件触发。

2、钩子函数参数的解释

handleRemove(file, fileList) {
	console.log(file, fileList);         
     },
handlePreview(file) {
	console.log(file);
      },
handChange(file,fileList){
	console.log(file);
	console.log(fileList)
      },
handleExceed(files, fileList) {
	  }

(1)file:最新选择的文件
(2)fileList:已经选择过的文件列表

3、提交按钮@click绑定axios请求向后台传递文件

(1)axios请求是在我们自定义的fileup()函数部分,表单的file和fileList的作用域并不包含该函数。

要在Vue的data属性中定义currentFile列表作为存储中介,存储表单选中的文件,并作为axios请求的参数。

(2)利用 FormData 对象,我们可以通过 JavaScript 用一些键值对(key-value)来模拟一系列表单控件。与普通的 Ajax 相比,使用 FormData 的最大优点就是我们可以异步上传二进制文件。

必须使用FormData对象携带二进制文件向后台提交数据

(3)必须传递文件的raw属性!!!!!!!

let key;
for (key in fileList){     //挨个添加文件
	//第1个参数是存储的key,第2个参数是存储的value
    formData.append("currentFile",fileList[key].raw)   //要传raw属性
     }

①往FormData对象中放file文件时不要直接把值传进去,而是传 raw 属性,这个才是文件。②往FormData对象中放file文件时, 若文件为多个,一定要一个一个进行添加,否则不会正确的传值。③若为多文件提交,append函数的第一个参数可以都设置为相同的,这样后台便于接收。

选择的文件的raw属性信息如下:

vue springboot excel的导入导出 springboot vue文件上传_vue


(4)axios请求如下

vue springboot excel的导入导出 springboot vue文件上传_java_02


请求体表单参数为multipart/form-data格式。

vue springboot excel的导入导出 springboot vue文件上传_vue_03


请求体的表单数据为二进制类型。

二、后台接收文件并存储磁盘

后端完整代码

@CrossOrigin(origins = "http://localhost:8080",maxAge = 3600)
    @PostMapping(value = "/fileup")
    //@RequestParam的参数值为前端FormData对象的存储内容的key值
    //多文件上传时,用MultipartFile[]数组进行接收,单文件也可以这样接收
    public String fileup(@RequestParam("currentFile") MultipartFile[] files, HttpServletRequest req) {
        System.out.println("接收到的文件有"+files.length+"个");
        for(MultipartFile f:files){
            System.out.println("正在存储"+f.getOriginalFilename()+"文件");
            String path = "e:/upload/";
            String name=f.getOriginalFilename();
            File floder=new File(path);
            if(!floder.isDirectory())
                floder.mkdirs();
            try{
                f.transferTo(new File(floder,name));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return "上传成功";
    }

1、配置文件设置接收文件参数

在配置文件application.properties中设置上传文件的参数:

spring.servlet.multipart.enabled=true	//是否开启文件上传支持,默认为true
spring.servlet.multipart.file-size-threshold=0	//文件写入磁盘的阈值,默认为0
spring.servlet.multipart.location=E:\\temp	//上传文件的临时保存位置
spring.servlet.multipart.max-file-size=1MB	//上传的单个文件的最大大小,默认为1MB
spring.servlet.multipart.max-request-size=10MB	//多文件上传时文件的总大小,默认为10MB
spring.servlet.multipart.resolve-lazily=false	//文件是否延迟解析,默认是false

2、数组接收多文件

(1)当前端传来多文件,后端用MultipartFile[]数组进行接收。
(2)在MultipartFile[]数组前面用@RequestParam("")指明该数组接收的FormData对象的存储内容的key值,因此前端在formData.append(“key”,value)添加文件时建议将所有上传文件的key设置成相同的,这样后端就可以只使用1个@RequestParam("key") MultipartFile[]接收所有的文件。

3、File文件操作

(1)创建文件

public File(String pathname)     //使用一个文件路径创建一个File类的对象
public File(String parent, String child)    //使用父路径和子路径结合代表文件路径

File f = new File(“d:\test\”,”1.txt”);
//这样代表的文件路径是:d:\test\1.txt。

(2)创建文件夹

File file=new File("D:\\test");
if(!file.exists()){     //如果文件夹不存在
	file.mkdir();      //创建文件夹

该方法的作用是创建当前文件文件夹,而不创建该路径中的其它文件夹。假设d盘下只有一个test文件夹,则创建d:\test\abc文件夹则成功,如果创建d:\a\b文件夹则创建失败,因为该路径中d:\a文件夹不存在。如果创建成功则返回true,否则返回false。

public boolean mkdirs()

该方法的作用是创建文件夹,如果当前路径中包含的父目录不存在时,也会自动根据需要创建

(3)createNewFile()

public boolean createNewFile() throws IOException

该方法的作用是创建指定的文件。该方法只能用于创建文件,不能用于创建文件夹,且文件路径中包含的文件夹必须存在。

(4)delete()

public boolean delete()

该方法的作用是删除当前文件或文件夹。如果删除的是文件夹,则该文件夹必须为空。如果需要删除一个非空的文件夹,则需要首先删除该文件夹内部的每个文件和文件夹,然后在可以删除,这个需要书写一定的逻辑代码实现。

(5)exists()

public boolean exists()

该方法的作用是判断当前文件或文件夹是否存在。

(6)isDirectory()

public boolean isDirectory()

该方法的作用是判断当前File对象是否是目录。

(7)isFile()

public boolean isFile()

该方法的作用是判断当前File对象是否是文件

(8)getAbsolutePath()

public String getAbsolutePath()

该方法的作用是获得当前文件或文件夹的绝对路径。例如c:\test\1.t则返回c:\test\1.t。

(9)getName()

public String getName()

该方法的作用是获得当前文件或文件夹的名称。例如c:\test\1.t,则返回1.t。

(10)length()

public long length()

该方法的作用是返回文件存储时占用的字节数。该数值获得的是文件的实际大小,而不是文件在存储时占用的空间数

(11)list()

public String[] list()

该方法的作用是返回当前文件夹下所有的文件名和文件夹名称。说明,该名称不是绝对路径。

(12)listFiles()

public File[] listFiles()

该方法的作用是返回当前文件夹下所有的文件对象。

4、MultipartFile文件操作

(1)getBytes()

public byte[] getBytes()

获取文件数据

(2)getContentType[]

public String getContentType[]

获取文件类型,如image/jpeg等

(3)getInputStream()

public InputStream getInputStream()

获取文件流

(4)getName()

public String getName()

获取表单中文件组件的名字

(5)getOriginalFilename()

public getOriginalFilename()

获取上传文件的原名

(6)getSize()

public Long getSize()

获取文件的字节大小,单位为byte

(7)isEmpty()

public boolean isEmpty()

确定是否有上传文件

(8)transferTo()

public void transferTo(File dest)

将上传文件保存到一个目录文件中