element-ui文件上传下载组件:
具备上传、下载和删除功能。
不自动上传,附件选择后只将文件加入待上传列表,点击确认上传按钮后上传到后台服务器,已上传的文件通过表格的形式展示在上方表格中。
删除和上传权限可配置。
效果如下:
代码如下:
/*
* @Description: element-ui文件上传下载组件(不自动上传)
* @Author: yiwenli
* @Date: 2023-03-30
*/
Vue.component('fileUpload', {
name: 'fileUpload',
template: /* html */ `<div>
<div>已上传文件列表</div>
<el-table :data="uploadedFileList" :show-header="false">
<el-table-column prop="fileName" width="300" align="center"></el-table-column>
<el-table-column prop="operate" width="200" align="center">
<template slot-scope="scope">
<el-button class="el-icon-download" type="text" @click="fileDownload(scope.row)">下载</el-button>
<el-button class="el-icon-delete" type="text" @click="fileDeleteInServer(scope.row)" v-if="deleteAble">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-form-item v-if="uploadAble">
<el-upload
action="#"
:auto-upload="false"
:multiple="true"
:file-list="toUploadFileList"
:on-change="fileChange"
:on-remove="fileRemove"
>
<el-button slot="trigger" size="small" type="primary">附件选择</el-button>
<el-button size="small" plain @click="fileUpload">确认上传</el-button>
</el-upload>
</el-form-item>
</div>`,
props: {
// 原始文件数据
fileList: {
type: Array,
required: true,
default: () => [],
},
// 上传功能是否可用
uploadAble: {
type: Boolean,
required: false,
default: true,
},
// 删除功能是否可用
deleteAble: {
type: Boolean,
required: false,
default: true,
},
},
data() {
return {
// 已上传文件列表
uploadedFileList: [],
// 待上传文件列表
toUploadFileList: [],
};
},
computed: {
// 上传路径
uploadUrl() {
return requestUrlBase('/file/fileUpload');
},
// 删除路径
deleteUrl() {
return requestUrlBase('/file/deleteFile');
},
// 下载路径
downloadUrl() {
return requestUrlBase('/file/downLoadFile');
},
},
watch: {
uploadedFileList() {
this.$emit('update:fileList', this.uploadedFileList);
},
fileList() {
this.uploadedFileList = this.fileList;
},
},
created() {
this.uploadedFileList = this.fileList;
},
methods: {
/**
* 文件上传
*/
fileUpload() {
if (this.toUploadFileList.length === 0) {
this.$message.error('请先选择至少1个文件再上传');
return;
}
let formData = new FormData();
this.toUploadFileList.forEach((item) => {
formData.append('file', item.raw);
});
$http({
url: this.uploadUrl,
method: 'post',
data: formData,
headers: { 'Content-Type': 'multipart/form-data' },
})
.then(({ data }) => {
// 1、更新已上传文件列表
this.uploadedFileList = this.uploadedFileList.concat(data);
// 2、更新待上传文件列表
let returnFileMap = new Map(data.map((value) => [value.fileName, value.fileId]));
this.toUploadFileList = this.toUploadFileList
.filter((item) => !returnFileMap.has(item.name))
.map((item) => {
item.status = 'error';
return item;
});
this.$message.success('附件上传完成');
})
.catch((e) => {
this.$message.error(e.message || '附件上传失败');
});
},
/**
* 文件修改
* @param {*} file 当前操作文件对象
* @param {*} fileList 文件列表
*/
fileChange(file, fileList) {
this.toUploadFileList = fileList;
},
/**
* 文件删除
* @param {Object} file 当前操作文件对象
* @param {Array} fileList 文件列表
*/
fileRemove(file, fileList) {
this.toUploadFileList = fileList;
},
/**
* 文件从服务器端删除
* @param {Object} file 文件对象
*/
fileDeleteInServer(file) {
this.$confirm('确定删除?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
$http({
url: this.deleteUrl,
method: 'get',
params: requestParam(file, 'get'),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
})
.then(() => {
this.$message.success('文件删除成功');
this.uploadedFileList.splice(
this.uploadedFileList.findIndex((item) => item.fileId === file.fileId),
1
);
})
.catch((e) => {
this.$message.error(e.message || '文件删除失败');
});
})
.catch(() => {
this.$message({
type: 'info',
message: '已取消删除',
});
});
},
/**
* 文件下载
* @param {Object} file 文件对象
*/
fileDownload(file) {
let params = {
fileId: file.fileId,
fileName: file.fileName,
userOrgId: Session.get('userInfo').orgId,
};
this.postDownloadFile(params, this.downloadUrl);
},
/**
* 以post方式传参并下载文件
* @param {Object} params 请求需要的参数
* @param {String} url 请求url地址
*/
postDownloadFile(params, url) {
// params是post请求需要的参数,url是请求url地址
const form = document.createElement('form');
form.style.display = 'none';
form.action = url;
form.method = 'post';
document.body.appendChild(form);
// 动态创建input并给value赋值
for (const key in params) {
const input = document.createElement('input');
input.type = 'hidden';
input.name = key;
input.value = params[key];
form.appendChild(input);
}
form.submit();
form.remove();
},
},
});
<file-upload
:file-list.sync="fileList"
:upload-able="true"
:delete-able="true"
></file-upload>
后端:
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.ieslab.model.Result;
import com.ieslab.pfjawebsvr.modules.file.domain.FileVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.Resource;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.*;
/**
* @author
* @ClassName FileOpController
* @Description 附件上传下载
* @date 2022年03月16日 14:20
*/
@Api(tags = "附件上传下载")
@RestController
@RequestMapping("/file")
@Slf4j
public class FileController {
@ApiOperation("附件上传")
@PostMapping("/fileUpload")
public Result upLoadFile(@RequestHeader("userOrgId") String userOrgId, @RequestParam("file") MultipartFile[] files) {
if (files.length == 0) {
return Result.error("文件为空");
}
if (ObjectUtil.isNull(userOrgId)) {
userOrgId = "null";
}
List<FileVO> fileVOList = new ArrayList<>();
try {
String basePath = System.getenv("PFJA_HOME") + "/file/" + userOrgId + "/";
for (MultipartFile multipartFile : files) {
String fileName = multipartFile.getOriginalFilename();
//生成文件唯一has256值
String hashValue = DigestUtil.sha256Hex(multipartFile.getBytes());
String filePath = basePath + FileNameUtil.getPrefix(fileName) + "&" + hashValue + "." + FileNameUtil.getSuffix(fileName);
File file = FileUtil.file(filePath);
//实现文件存储
FileUtil.writeBytes(multipartFile.getBytes(), file);
fileVOList.add(new FileVO(hashValue, fileName));
}
return Result.ok(fileVOList);
} catch (Exception e) {
return Result.error(e.getMessage());
}
}
@ApiOperation("附件下载")
@PostMapping(value = "/downLoadFile")
public void downLoadFile(@RequestParam("userOrgId") String userOrgId, @RequestParam("fileId") String fileId, @RequestParam("fileName") String fileName, HttpServletResponse response) {
if (ObjectUtil.isNull(userOrgId)) {
userOrgId = "null";
}
String basePath = System.getenv("PFJA_HOME") + "/file/" + userOrgId + "/";
try {
// 创建输出流对象
ServletOutputStream outputStream = response.getOutputStream();
//以字节数组的形式读取文件
String filePath = basePath + FileNameUtil.getPrefix(fileName) + "&" + fileId + "." + FileNameUtil.getSuffix(fileName);
byte[] bytes = FileUtil.readBytes(filePath);
// 设置返回内容格式
response.setContentType("application/octet-stream");
// 把文件名按UTF-8取出并按ISO8859-1编码,保证弹出窗口中的文件名中文不乱码
// 中文不要太多,最多支持17个中文,因为header有150个字节限制。
// 这一步一定要在读取文件之后进行,否则文件名会乱码,找不到文件
fileName = new String(fileName.getBytes("UTF-8"), "UTF-8");
// 设置下载弹窗的文件名和格式(文件名要包括名字和文件格式)
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
// 返回数据到输出流对象中
outputStream.write(bytes);
// 关闭流对象
IoUtil.close(outputStream);
} catch (Exception e) {
Result.error(e.getMessage());
}
}
@ApiOperation("附件删除")
@GetMapping("/deleteFile")
public Result deleteFile(@RequestHeader("userOrgId") String userOrgId, @RequestParam("fileId") String fileId, @RequestParam("fileName") String fileName) {
String basePath = System.getenv("PFJA_HOME") + "/file/" + userOrgId + "/";
String filePath = basePath + FileNameUtil.getPrefix(fileName) + "&" + fileId + "." + FileNameUtil.getSuffix(fileName);
File file = FileUtil.file(filePath);
if (file.exists()) {
boolean delete = file.delete();
return Result.ok(delete);
} else {
return Result.ok(true);
}
}
}