Minio实现文件上传、下载、预览、删除

news/2024/9/24 18:04:45

 

环境:

 JDK11

 Minio8

 

 服务器搭建Minio:https://www.cnblogs.com/warmNest-llb/p/18233203

 

完成项目

AjaxResult 结果返回使用的 若依。

1. pom.xml

        <!-- MinIO Client --><dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.2.2</version></dependency>

2. application.yml

# Spring配置
spring:# minio配置servlet:multipart:enabled: true# 单个文件大小max-file-size: 100MB# 设置总上传的文件大小max-request-size: 200MBlocation: /usr/local/minio/data_file/temp_file/

 注意:配置的存放文件的路径。

# minio配置
minio:endpoint: http://120.服务器:9001
  accessKey: minioadminsecretKey: minioadminbucket: jx-file

 注意:endpoint- ip + 端口

    账号 密码

    bucket-创建的桶

 

3. Minio工具类

/*** MinIO 服务*/
@Component
public class MinIOUtil {// 指定MinIO服务的访问地址(包括协议、域名或IP以及端口)private static String endpoint;// MinIO的访问密钥(Access Key),用于身份验证private static String accessKey;// MinIO的秘密密钥(Secret Key),与访问密钥配对使用,也是认证的一部分。private static String secretKey;// 指定默认的存储桶(Bucket)名称,MinIO中用于组织和存储对象(文件)的基本容器。private static String bucket;@Value("${minio.endpoint}")public void setEndpoint(String endpoint) {MinIOUtil.endpoint = endpoint;}@Value("${minio.accessKey}")public void setAccessKey(String accessKey) {MinIOUtil.accessKey = accessKey;}@Value("${minio.secretKey}")public void setSecretKey(String secretKey) {MinIOUtil.secretKey = secretKey;}@Value("${minio.bucket}")public void setBucket(String bucket) {MinIOUtil.bucket = bucket;}/*** 创建并返回一个配置好的MinioClient实例* 用于与MinIO服务器交互,上传文件、下载文件、删除文件** @return 配置*/private static MinioClient getMinioClient() {return MinioClient.builder().endpoint(endpoint).credentials(accessKey, secretKey).build();}/*** 上传:将一个输入流中的文件上传到MinIO服务器上指定的存储桶(bucket)里** @param objectName  .object(objectName)指定了上传后对象的名称。* @param inputStream 转换为 流* @param size        文件大小* @param contentType 内容类型* @throws Exception 异常*/public static void uploadFile(String objectName, InputStream inputStream, long size, String contentType) throws Exception {MinioClient minioClient = getMinioClient();minioClient.putObject(// .bucket(bucket) 指定了目标存储桶的名称。.object(objectName)指定了上传后对象的名称。PutObjectArgs.builder().bucket(bucket).object(objectName).stream(inputStream, size, -1)// .contentType(contentType)指定了上传文件的内容类型。
                        .contentType(contentType).build());}/*** 下载:从MinIO服务器下载指定存储桶(bucket)中的文件** @param objectName 指定要从MinIO下载的文件对象名称(即文件路径和文件名)。* @return 流 对象* @throws Exception 异常*/public static InputStream downloadFile(String objectName) throws Exception {MinioClient minioClient = getMinioClient();return minioClient.getObject(GetObjectArgs.builder()// .bucket(bucket)指定了文件所在的存储桶名称。
                        .bucket(bucket)// .object(objectName)指定了要下载的对象名称。
                        .object(objectName).build());}/*** 预览:生成一个预签名的URL,允许用户通过浏览器或其他HTTP客户端以GET方法访问MinIO存储桶中指定对象(文件)的临时链接** @param objectName 指定需要获取预览链接的文件对象名称(包括路径)。* @return 对象* @throws Exception .method(Method.GET)指定了请求的方法为GET,这是预览文件时的标准HTTP方法。*                   .bucket(bucket)指定了存储桶的名称。*                   .object(objectName)指定了对象(文件)的名称。*/public static String getPreviewUrl(String objectName) throws Exception {MinioClient minioClient = getMinioClient();return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.GET).bucket(bucket).object(objectName).build());}/*** 删除:从MinIO服务器上的指定存储桶中删除一个文件** @param objectName 指定要删除的文件对象名称(包括路径和文件名)* @throws Exception*/public static void deleteFile(String objectName) throws Exception {MinioClient minioClient = getMinioClient();minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucket).object(objectName).build());}
}
MinIOUtil

 

4. 实体类

 

/*** 文件的实体类*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class JxAttachment {/** 文件ID */@TableId(value = "fileId")private Integer fileId;/** 文件名称 */private String fileName;/** 文件大小 */private String fileSize;/** 上传人员 */private String uploadingStaff;/** 上传时间 */private String uploadTime;/** 文件新名词 */private String fileNewName;/** 文件地址 */private String fileAddress;/** 部门编号 */private String deptNumber;}
JxAttachment-文件表

 

/*** 文件预览DTO*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class JxAttachmentPreviewDTO {/** 文件ID */private Integer fileId;/** 文件名称 */private String fileName;/** 文件大小 */private String fileSize;/** 上传人员 */private String uploadingStaff;/** 文件地址 */private String fileAddress;/** 文件新名词 */private String fileNewName;/** 上传时间 */private String uploadTime;}
JxAttachmentPreviewDTO-预览对象

 

5. Mapper层

@Mapper
public interface JxFileMapper {/** 添加上传信息 */int insertFiles(JxAttachment attachment);/** 部门编号 查询文件信息 */List<JxAttachmentPreviewDTO> previewFileByDeptNum(String deptNumber);/** 文件删除 */void deleteFile(@Param("fileName") String fileName, @Param("deptNumber") String deptNumber);
}<!-- 添加上传信息 --><insert id="insertFiles">insert into jx_attachment (file_id, file_name, file_size, uploading_staff, upload_time, file_new_name,file_address, dept_number)values (#{fileId}, #{fileName}, #{fileSize}, #{uploadingStaff}, #{uploadTime}, #{fileNewName}, #{fileAddress},#{deptNumber})</insert><!-- 部门编号 查询文件信息 --><select id="previewFileByDeptNum" resultType="com.ruoyi.xinzhi.model.dto.JxAttachmentPreviewDTO">select file_id         as fileId,file_name       as fileName,file_size       as fileSize,uploading_staff as uploadingStaff,file_address    as fileAddress,upload_time     as uploadTime,file_new_name   as fileNewNamefrom jx_attachmentwhere dept_number = #{deptNumber};</select><!-- 文件删除 --><delete id="deleteFile">delete from jx_attachment where dept_number = #{deptNumber} and file_new_name = #{fileName};</delete>
mapper

 PS:预览功能使用了部门编号进行作为条件。可做参考。

    删除功能使用了部门编号与文件名配合。

6. Service业务层

 只需要实现类与Controller交互

/*** 文件上传、下载、预览、删除*/
@Service
public class JxFileService {@Autowiredprivate JxUserMapper jxUserMapper;@Autowiredprivate JxFileMapper jxFileMapper;@Value("${spring.servlet.multipart.location}")private String tempDir;/*** 文件上传** @param file       文件属性* @param deptNumber 部门编号* @return 成功 ? 失败* @throws Exception*/@Transactionalpublic AjaxResult uploadFile(MultipartFile file, String deptNumber) throws Exception {String filename = file.getOriginalFilename();   // 获取文件名long size = file.getSize(); // 文件大小
File tempDirectory = new File(tempDir);if (!tempDirectory.exists()) {tempDirectory.mkdir();}try (InputStream inputStream = file.getInputStream()) {// 获取文件扩展名String fileExtension = FilenameUtils.getExtension(filename);// 获取不带后缀文件名String baseName = FilenameUtils.getBaseName(filename);// 生成5位时间戳String timestamp = new SimpleDateFormat("ssSSS").format(new Date());// 生成唯一文件名String fileName = baseName + "_" + timestamp + "." + fileExtension;// 文件的路径String fileAddress = "/usr/local/minio/data_file/" + fileName;// 文件上传到 minio 客户端
            MinIOUtil.uploadFile(fileName, inputStream, size, file.getContentType());// 使用 部门编号 获取对应 用户String userFullName = jxUserMapper.selectUserFullName(deptNumber);// 将当前格式转为年月日String date = formatDate(new Date());// 文件大小转换String fileSize = formatFileSize(size);// 信息存入数据库JxAttachment attachment = JxAttachment.builder().fileName(filename).fileSize(fileSize).uploadingStaff(userFullName).uploadTime(date).fileNewName(fileName).fileAddress(fileAddress).deptNumber(deptNumber).build();// 添加数据库
            jxFileMapper.insertFiles(attachment);return AjaxResult.success("上传成功");} catch (Exception e) {return AjaxResult.error("上传有误" + e.getMessage());}}/*** 预览文件* @param deptNumber 部门编号* @return 文件预览路径* @throws Exception 异常*/public AjaxResult previewFile(String deptNumber) throws Exception {// 部门编号 查询文件信息List<JxAttachmentPreviewDTO> attachments = jxFileMapper.previewFileByDeptNum(deptNumber);for (JxAttachmentPreviewDTO attachment : attachments) {String fileName = attachment.getFileNewName(); // 文件名String fileUrl = MinIOUtil.getPreviewUrl(fileName); // 生成文件路径attachment.setFileAddress(fileUrl); // 文件地址设置为预览地址
        }return AjaxResult.success(attachments);}/*** 文件大小转换* @param size 文件大小* @return 文件大小*/private String formatFileSize(long size) {DecimalFormat decimalFormat = new DecimalFormat("0.00");if (size < 1024) {return size + "B";} else if (size < 1024 * 1024) {return decimalFormat.format((double) size / 1024) + "KB";} else {return decimalFormat.format((double) size / (1024 * 1024)) + " MB";}}/*** 将 Date 转换为 yyyy-MM-dd 格式的字符串** @param date 当前时间* @return 方法*/private String formatDate(Date date) {SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");return formatter.format(date);}
}
service

 

7. Controller控制层

/*** 文件*/
@RestController
@RequestMapping("/file")
public class JxFileController {@Autowiredprivate JxFileService jxFileService;@Autowiredprivate JxFileMapper jxFileMapper;/*** 文件上传** @param file       文件属性* @param deptNumber 部门编号* @return 成功 ? 失败*/@PostMapping("/upload")public AjaxResult uploadFile(@RequestPart @RequestParam("file") MultipartFile file, @RequestParam("deptNumber") String deptNumber) {try {return jxFileService.uploadFile(file, deptNumber);} catch (MultipartException e) {throw new BusinessException("请求错误");} catch (Exception e) {throw new BusinessException("上传有误");}}/*** 文件下载* @param fileName 文件名* @param response 响应* @return 成功 ? 失败*/@GetMapping("/download")@ResponseBodypublic void downloadFile(@RequestParam String fileName, HttpServletResponse response) {try (InputStream inputStream = MinIOUtil.downloadFile(fileName)) {// 文件名进行编码String encodedFileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");// 设置响应内容类型response.setContentType("application/octet-stream");// 设置响应头,告知浏览器进行下载处理response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodedFileName);// 获取响应输出流ServletOutputStream outputStream = response.getOutputStream();// 文件内容写入响应输出流byte[] buffer = new byte[1024];int bytesRead;while ((bytesRead = inputStream.read(buffer)) != -1) {outputStream.write(buffer, 0, bytesRead);}outputStream.flush();} catch (IOException e) {throw new BusinessException("IO异常");} catch (Exception e) {throw new BusinessException("下载有误");}}/*** 预览文件** @param deptNumber 部门编号* @return 文件预览路径*/@GetMapping("/preview")public AjaxResult previewFile(@RequestParam String deptNumber) {try {AjaxResult result = jxFileService.previewFile(deptNumber);return AjaxResult.success("预览路径", result);} catch (Exception e) {throw new BusinessException("生成路径有误");}}/*** 删除文件* @param fileName 文件名* @return 成功 ? 失败* @throws Exception 异常*/@DeleteMapping("/delete")private AjaxResult deleteFile(@RequestParam String fileName, String deptNumber){try{MinIOUtil.deleteFile(fileName);jxFileMapper.deleteFile(fileName, deptNumber);return AjaxResult.success("删除成功");} catch (Exception e) {throw new BusinessException("删除有误");}}
}
Controller

 

8. 测试

PS:文件可以存储多种格式的。但越大响应速度越慢。

1) 上传

参数:file 文件;deptNumber 部门编号

在上传时,使用当前时间时间戳生成5为数,防止文件名重复。

 

 

 

成功 为返回。

上传文件时,传入服务器,同时生成 别的信息存储数据库。 

可以看到 服务器 与 桶 里也存在。

 

2) 预览

传参:deptNumber 部门编号去查询到对应的文件。

 

返回的是:fileAddress 文件路径。使用文件路径可以直接访问预览。 

查到的两个文件,使用链接浏览器是可以访问的。

 

3) 下载

下载时 使用服务器存储的文件名。(在上传时生成的文件名+时间戳+后缀)

 与浏览器做了响应,postman无法测试。

4) 删除

此时服务器、桶、数据库存储了两个数据。

参数:文件名,使用服务器存储的文件名。(在上传时生成的文件名+时间戳+后缀)

   部门编号

删除pdf文件。

 

9. 联调

前端:vue2 + ElementUI

一个上传按钮(未截图) + 四个图标按钮。

    <!-- 上传文件按钮 --><el-uploadref="upload"action="":http-request="uploadFile":before-upload="beforeUpload":show-file-list="false"><el-buttontype="primary"class="vertical-center"style="background-color: rgb(2, 167, 240);height: 40px;margin-right: 10px;"@click="handleUpload">上传文件</el-button></el-upload>
上传文件按钮

 

  <div><!-- 表格里的字段 --><el-table :data="tableData" border style="width: 100%"><!-- 添加复选框 --><el-table-column type="selection" width="55"></el-table-column><!-- 表格列 --><el-table-columnfixedprop="fileName"label="文件名称"border></el-table-column><el-table-columnprop="fileSize"label="文件大小"show-overflow-tooltipborderwidth="200px"></el-table-column><el-table-columnprop="uploadingStaff"label="上传人员"borderwidth="200px"></el-table-column><el-table-columnprop="uploadTime"label="上传时间"show-overflow-tooltipborderwidth="200px"></el-table-column><!-- slot-scope="scope" --><el-table-column prop="operation" label="操作" border width="200px"><template v-slot:default="scope"><div class="operation-icons"><!-- 查看按钮 @click="handleView(scope.row)"--><el-tooltip content="查看" placement="bottom"><el-buttontype="text"icon="el-icon-search"@click="handleView(scope.row)"></el-button></el-tooltip><!-- 删除按钮  @click="handleDelete(scope.row)"--><el-tooltip content="删除" placement="bottom"><el-buttontype="text"icon="el-icon-delete"@click="handleDelete(scope.row)"></el-button></el-tooltip><!-- 下载按钮 @click="handleDownload(scope.row)"--><el-tooltip content="下载" placement="bottom"><el-buttontype="text"icon="el-icon-download"@click="handleDownload(scope.row)"></el-button></el-tooltip><!-- 上传按钮 @click="handleUpload"--><el-tooltip content="上传" placement="bottom"><el-uploadref="upload"action="":http-request="uploadFile":before-upload="beforeUpload":show-file-list="false"@change="handleUpload"><el-buttontype="text"icon="el-icon-upload2"@click="handleUpload"></el-button></el-upload></el-tooltip></div></template></el-table-column></el-table></div>
HTML
.operation-icons {display: flex;justify-content: space-around;align-items: center;width: 150px; /* 宽度调整  */
}

 

四个方法:

@click="handleUpload" -- 上传
@click="handleDownload(scope.row)" -- 下载
@click="handleView(scope.row)" -- 预览
@click="handleDelete(scope.row)" -- 删除

 

    // 文件上传
    async uploadFile(option) {// 可以以同步的方式编写异步代码,暂停函数的执行,等待一个Promise解析(resolve或reject),然后继续执行函数。const formData = new FormData();formData.append("file", option.file);try {const response = await request.post("http://localhost:8080/file/upload",formData);if (response.code === 200) {this.$message.success("成功上传");this.fetchTableDate(); // 重新加载表格数据} else {this.$message.error(`上传失败: ${response ? response.message : "未知错误"}`);}} catch (error) {this.$message.error(`上传失败: ${error.message}`);}},// 文件上传
    handleUpload() {this.$refs.upload.submit(); // 引用为upload的表单元素并提交
    },// 预上传处理函数
    beforeUpload(file) {return true; // 可以在此处添加例如检查文件类型、大小等逻辑},
上传-@click="handleUpload"

 

    // 文件预览 触发查询
    fetchTableDate() {request.get("http://localhost:8080/file/preview").then((res) => {this.tableData = res.data.data;}).catch((error) => {this.$message.error("获取文件有误", error);});},// 点击预览
    handleView(row) {const fileUrl = row.fileAddress;window.open(fileUrl, "_blank"); // 在新窗口中打开预览URL},
预览-@click="handleView(scope.row)"

 

    // 文件下载
    handleDownload(row) {const fileName = row.fileNewName;// 发起请求到后端下载
      request({url: "http://localhost:8080/file/download",method: "get",responseType: "blob", // 指定类型为 blob
        params: { fileName },}).then((res) => {// this.$message.success(res.msg);// 创建一个 URL 链接到文件内容const url = window.URL.createObjectURL(res);const link = document.createElement("a");link.href = url;link.setAttribute("download", fileName); // 设置下载属性
          document.body.appendChild(link);link.click();document.body.removeChild(link);window.URL.revokeObjectURL(url); // 释放URL对象
        }).catch((error) => {this.$message.error("下载有误", error.message);});},
下载-@click="handleDownload(scope.row)"

 

    // 删除文件
    handleDelete(row) {const fileName = row.fileNewName; // 文件名称console.log("删除文件--", fileName);this.$confirm("确定文件永久删除吗,是否继续", "提示", {confirmButtonText: "确定",cancelButtonText: "取消",type: "warning",}).then(() => {request({url: "http://localhost:8080/file/delete",method: "delete",params: { fileName },}).then((res) => {this.$message.success(res.msg);this.fetchTableDate(); // 重新加载表格数据
            }).catch((error) => {this.$message.error(res.msg);});}).catch((error) => {this.$message.error("文件删除有误,请稍后重试");});},
删除-@click="handleDelete(scope.row)"

 

点击上传成功。

 

完成代码:

# 自定义的请求。请求头内存储了token与部门编号
import request from "@/utils/request";
// 导入axios库,用于发起HTTP请求
import axios from 'axios';// 使用axios.create方法创建一个axios实例,以便进行定制化配置
const service = axios.create({// 设置基础URL,所有使用此axios实例发起的请求都会以此作为前缀// 部署服务器时替换服务器域名baseURL: 'http://localhost:8089',// 设置请求超时时间,单位为毫秒。此处设置为150000毫秒,即15秒timeout: 150000
});// 添加请求拦截器。此拦截器会在每个请求发送出去之前被调用
service.interceptors.request.use(// 这个函数会在请求发出前执行request => {// 从localStorage中尝试获取token,通常在用户登录后存储const token = localStorage.getItem('token');// 从localStorage 中获取 部门编号const deptNumber = localStorage.getItem("deptNumber");// 如果token存在,则将其添加到请求头中,以便服务器进行权限验证// 注意:这里使用'token'作为请求头的key可能需要根据后端要求调整,常见的还有'Authorization'if (token) {request.headers.token = token;}// 如果部门ID存在,将其添加到请求参数中if (deptNumber) {if (request.method === 'get' || request.method === 'delete') {// GET 请求的参数request.params = { ...request.params, deptNumber };} else if (request.method === 'post' || request.method === 'put') {if (request.data instanceof FormData) {request.data.append('deptNumber', deptNumber);} else {// POST 请求的参数request.data = { ...request.data, deptNumber };}}}// 必须返回修改后的请求配置,以便axios继续处理return request;},// 如果在请求拦截器中抛出了错误,此函数会被调用error => {// 将错误传递给下一个处理错误的逻辑(例如响应拦截器或全局错误处理)return Promise.reject(error);}
);// 添加响应拦截器。此拦截器会在每个请求的响应到达后被调用
service.interceptors.response.use(// 当请求成功时(HTTP状态码为2xx),此函数会被调用// response => {response => response.data,error => {if (error.response) {const { code, message } = error.response.data;console.error(`Error ${code}: ${message}`);if (code === 401) {alert(message);// 跳转到登录页面或执行其他操作router.push('/login');}}return Promise.reject(error);}
);// 导出定制化的axios实例,供其他模块使用
export default service;
自定义request请求

 

<template><div><!-- 上传文件按钮 --><el-uploadref="upload"action="":http-request="uploadFile":before-upload="beforeUpload":show-file-list="false"><el-buttontype="primary"class="vertical-center"style="background-color: rgb(2, 167, 240);height: 40px;margin-right: 10px;"@click="handleUpload">上传文件</el-button></el-upload><!-- 表格里的字段 --><el-table :data="tableData" border style="width: 100%; margin-top: 20px"><!-- 添加复选框 --><el-table-column type="selection" width="55"></el-table-column><!-- 表格列 --><el-table-columnfixedprop="fileName"label="文件名称"border></el-table-column><el-table-columnprop="fileSize"label="文件大小"show-overflow-tooltipborderwidth="200px"></el-table-column><el-table-columnprop="uploadingStaff"label="上传人员"borderwidth="200px"></el-table-column><el-table-columnprop="uploadTime"label="上传时间"show-overflow-tooltipborderwidth="200px"></el-table-column><!-- slot-scope="scope" --><el-table-column prop="operation" label="操作" border width="200px"><template v-slot:default="scope"><div class="operation-icons"><!-- 查看按钮 @click="handleView(scope.row)"--><el-tooltip content="查看" placement="bottom"><el-buttontype="text"icon="el-icon-search"@click="handleView(scope.row)"></el-button></el-tooltip><!-- 删除按钮  @click="handleDelete(scope.row)"--><el-tooltip content="删除" placement="bottom"><el-buttontype="text"icon="el-icon-delete"@click="handleDelete(scope.row)"></el-button></el-tooltip><!-- 下载按钮 @click="handleDownload(scope.row)"--><el-tooltip content="下载" placement="bottom"><el-buttontype="text"icon="el-icon-download"@click="handleDownload(scope.row)"></el-button></el-tooltip><!-- 上传按钮 @click="handleUpload"--><el-tooltip content="上传" placement="bottom"><el-uploadref="upload"action="":http-request="uploadFile":before-upload="beforeUpload":show-file-list="false"@change="handleUpload"><el-buttontype="text"icon="el-icon-upload2"@click="handleUpload"></el-button></el-upload></el-tooltip></div></template></el-table-column></el-table></div>
</template><script>
import request from "@/utils/request";export default {data() {return {tableData: [],};},mounted() {// 预览挂载this.fetchTableDate();},methods: {// 文件上传
    async uploadFile(option) {// 可以以同步的方式编写异步代码,暂停函数的执行,等待一个Promise解析(resolve或reject),然后继续执行函数。const formData = new FormData();formData.append("file", option.file);try {const response = await request.post("http://localhost:8080/file/upload",formData);if (response.code === 200) {this.$message.success("成功上传");this.fetchTableDate(); // 重新加载表格数据} else {this.$message.error(`上传失败: ${response ? response.message : "未知错误"}`);}} catch (error) {this.$message.error(`上传失败: ${error.message}`);}},// 文件上传
    handleUpload() {this.$refs.upload.submit(); // 引用为upload的表单元素并提交
    },// 预上传处理函数
    beforeUpload(file) {return true; // 可以在此处添加例如检查文件类型、大小等逻辑
    },// 文件预览 触发查询
    fetchTableDate() {request.get("http://localhost:8080/file/preview").then((res) => {this.tableData = res.data.data;}).catch((error) => {this.$message.error("获取文件有误", error);});},// 点击预览
    handleView(row) {const fileUrl = row.fileAddress;window.open(fileUrl, "_blank"); // 在新窗口中打开预览URL
    },// 文件下载
    handleDownload(row) {const fileName = row.fileNewName;// 发起请求到后端下载
      request({url: "http://localhost:8080/file/download",method: "get",responseType: "blob", // 指定类型为 blob
        params: { fileName },}).then((res) => {// this.$message.success(res.msg);// 创建一个 URL 链接到文件内容const url = window.URL.createObjectURL(res);const link = document.createElement("a");link.href = url;link.setAttribute("download", fileName); // 设置下载属性
          document.body.appendChild(link);link.click();document.body.removeChild(link);window.URL.revokeObjectURL(url); // 释放URL对象
        }).catch((error) => {this.$message.error("下载有误", error.message);});},// 删除文件
    handleDelete(row) {const fileName = row.fileNewName; // 文件名称console.log("删除文件--", fileName);this.$confirm("确定文件永久删除吗,是否继续", "提示", {confirmButtonText: "确定",cancelButtonText: "取消",type: "warning",}).then(() => {request({url: "http://localhost:8080/file/delete",method: "delete",params: { fileName },}).then((res) => {this.$message.success(res.msg);this.fetchTableDate(); // 重新加载表格数据
            }).catch((error) => {this.$message.error(res.msg);});}).catch((error) => {this.$message.error("文件删除有误,请稍后重试");});},},
};
</script><style scoped>
.operation-icons {display: flex;justify-content: space-around;align-items: center;width: 150px; /* 宽度调整  */
}.operation-icons .el-button {padding: 0;
}
</style>
完整代码

点击下载,通过浏览器下载本地路径。

 

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

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

相关文章

一个ArcGIS中我知道但是不常用的工具

如题,这个工具好像很有用 Raster To Point

PHP 5.4 编译 configure: error: libXpm.(a|so) not found.

https://blog.csdn.net/niugang123/article/details/84972934

[模式识别复习笔记] 第8章 决策树

1. 决策树 1.1 决策树简介 决策树(Decision Tree)是一种以 树形数据结构 来展示决策规则和分类结果的模型。每一条从根结点(对最终分类结果贡献最大的属性)到叶子结点(最终分类结果)的路径都代表一条决策的规则。1.2 决策树的构建过程首先生成一个 根节点,其 包含所有的…

CM3调试系统简析

包括对两大调试接口:JTAG接口和SWD串行线调试接口、CoreSight调试接口:基于CoreSight架构的CM3调试系统和标准CoreSight架构和CM3中调试系统异同点、CoreSight跟踪接口、 调试功能的总结、调试模式、调试事件、STM32调试单元、SWV调试、JTAG边界扫描 、代码断点类型等知识点的…

KOPRA论文阅读笔记

Joint Knowledge Pruning and Recurrent Graph Convolution for News Recommendation论文阅读笔记 Abstract ​ 最近,利用知识图谱(KG)来丰富新闻文章的语义表征已被证明对新闻推荐有效。这些解决方案的重点是利用知识图谱中的附加信息对新闻文章进行表征学习,用户表征主要…

R语言估计时变VAR模型时间序列的实证研究分析案例|附代码数据

原文链接: http://tecdat.cn/?p=3364 原文出处:拓端数据部落公众号最近我们被客户要求撰写关于时变VAR模型的研究报告,包括一些图形和统计输出。 加载R包和数据集加载包后,我们将此数据集中包含的12个心情变量进行子集化: mood_data <- as.matrix(symptom_data$data[…

Modbus协议转Profibus协议网关模块连PLC与激光发射器通讯

PLC作为控制中枢,而Modbus协议和Profibus协议是两种常见的工业通讯协议。在实际应用中,我们常常会遇到需要将Modbus协议转换为Profibus协议的情况,这时就需要借助Modbus协议转Profibus协议网关模块(XD-MDPB100)来进行实现。Modbus协议转Profibus协议网关模块可以理解为一种…

生态学建模:增强回归树(BRT)预测短鳍鳗生存分布和影响因素|附代码数据

全文下载链接: http://tecdat.cn/?p=22482 最近我们被客户要求撰写关于增强回归树(BRT)的研究报告,包括一些图形和统计输出。 在本文中,在R中拟合BRT(提升回归树)模型。我们的目标是使BRT(提升回归树)模型应用于生态学数据,并解释结果。 引言 本教程的目的是帮助你学…