【Java项目】Vue+ElementUI+Ceph实现多类型文件上传功能

news2024/11/20 14:22:47

文章目录

  • 前端
  • 后端Java

前端

ElementUI
文件上传的页面使用的是ElementUI的
在这里插入图片描述
下面是index.vue页面,有点小bug(但是我真改不动,前端还在学习中)


<template>
    <div>
      <el-upload
        class="upload-demo"
        :action="uploadAction"
        :on-remove="handleRemove"
        :on-success="handleSuccess"
        :on-error="handleError"
        :before-upload="beforeUpload"
        multiple
        :file-list="fileList">
        <el-button size="small" type="primary">点击上传</el-button>
        <div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div>
      </el-upload>
    </div>
</template>
<script setup name="uploadImage">
import { ref, defineProps, onMounted, defineEmits } from 'vue';
import { ElMessage } from 'element-plus';
const props = defineProps(['modelValue'])
const fileList = ref([])
const uploadAction = ref('/merchant/api/common/upload/image')
const emits = defineEmits(['update:modelValue','input'])

onMounted(() => {
    setDefaultFileList()
})
const setDefaultFileList = () => {
    console.log('modelValue', props.modelValue)
    if (props.modelValue && props.modelValue.length > 0) {
        fileList.value = []
        let flag = false;
        props.modelValue.forEach(element => {
          //判断当前请求名称是否是name
          //如果是name那么之后下一个数据才是文件名称
          //然后显示文件名称
          if(flag){
            fileList.value.push({
              // url: element,
              name: element,
            })
          }
          flag=false;
          if(element=="name"){
            flag = true;
          }
        });
    }
}
const handleRemove = (file, fileList) => {
    let fileList1 = fileList.map(item => item.response.data)
    emits("update:modelValue", fileList1);
}

//文件上传还有一点bug 
//如果不把所有文件都删掉,只删除部分的那么上传失败
const handleSuccess = (response, file, fileList) => {
    let fileList1 = fileList.map(item => item.response.data)
    emits("update:modelValue", fileList1);
}

const handleError = (error) => {
    console.log('handleError', error)
    ElMessage.error('文件上传失败');
}

const beforeUpload = (file) => {
    // console.log(file)
    const isLt2M = file.size / 1024 / 1024 < 20;
    if (!isLt2M) {
        ElMessage.error('上传文件大小不能超过 20MB!');
    }
    return isLt2M;
}
</script>

大概情况就是我们添加文件的时候,会发送一个请求到后端,这个后端的路径为

const uploadAction = ref('/merchant/api/common/upload/image')

当点击上传图片之后,我们的网页会发送这个请求,这个请求其实是会被router处理的,如下

var express = require("express");
var router = express.Router();
var request = require("superagent");
var multer = require("multer");

module.exports = (app) => {
  /**
   * 上传图片
   */
  router.post("/upload/image", async (req, res, next) => {
    var storage = multer.memoryStorage();
    var upload = multer({ storage }).single("file");
    upload(req, res, async (err) => {
      try {
        const url = "http://localhost:8081/supplier/outerapi/api/ceph/upload";
        const response = await request.post(url).attach("file", req.file.buffer, req.file.originalname);
        const result = JSON.parse(response.text);
        res.send({
          code: 200,
          msg: "图片上传成功",
          data: {
            name: result.data[0].name,
            url: result.data[0].url,
          },
        });
      } catch (err) {
        res.send({
          code: 500,
          msg: "图片上传异常",
        });
      }
    });
  });
  //使用/merchant/api/common作为路径前缀
  app.use("/merchant/api/common", router);
};

可以看到这里有一个url,这个url就是你的后端处理前端文件上传的那个接口了。
并且可以看到我们的返回类型要求是code,msg,data,其中data要求有name和url,分别是文件名称和文件路径。

后端Java

我们先来看控制层代码,这里我们的OSS服务使用的是ceph,你也可以使用minio等来代替。

 /**
     * 上传订单附件接口,文件可以一次传多个
     * Content-Type:multipart/form-data;
     * 文件参数名:file
     *
     * @param files 请求的文件
     * @return 返回
     */
    @PostMapping("/upload")
    public BaseResponse<List<FileInfo>> uploadFile(@RequestParam("file")
                                                     List<MultipartFile> files) {
        BaseResponse<List<FileInfo>> res = new BaseResponse<List<FileInfo>>();
        res.setCode(500);
        res.setMsg("上传失败,请稍后重试");
        ArrayList<FileInfo> listFile = new ArrayList<FileInfo>();
        try {
            //遍历请求头里的文件
            //for (int i = 0; i < files.size(); i++) {
            for (MultipartFile file : files) {
                //获取文件的原始文件名
                String fileName = file.getResource().getFilename();
                //获取文件后缀,如 .jpg
                String fileSuffix = fileName.substring(fileName.lastIndexOf("."));
                //ceph请求参数
                CephRequest cephRequest = new CephRequest();
                cephRequest.originFileName = fileName;
                //文件名设置一个随机数,避免重复
                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
                String uuid = UUID.randomUUID().toString();
                cephRequest.setFileName(dateFormat.format(new Date())
                        + uuid.substring(uuid.lastIndexOf("-")) + fileSuffix);
                //这里可以设置块名称也可以不设置,不设置用默认的
                cephRequest.setBlockName("");
                cephRequest.setMaxDays(5 * 365); //五年
                /* 设置请求头信息 */
                cephRequest.setContextType(file.getContentType());
                // 从OkHttp里面提交的postfileName 需要根据实际文件后缀修改contentType
                if (cephRequest.getContextType().equals("application/from-data")) {
                    if (fileName.endsWith(".pdf")) {
                        cephRequest.setContextType("application/pdf");
                    } else if (fileName.endsWith(".jpeg")) {
                        cephRequest.setContextType("image/jpeg");
                    } else if (fileName.endsWith(".png")) {
                        cephRequest.setContextType("image/png");
                    } else if (fileName.endsWith(".bmp")) {
                        cephRequest.setContextType("image/bmp");
                    } else if (fileName.endsWith(".jpg")) {
                        cephRequest.setContextType("image/jpg");
                    }
                }
                cephRequest.setFileStream(file.getInputStream());
                LogUtil.info(JSON.toJSONString(cephRequest), "cephRequest");
                //上传文件到ceph
                var cephResponse = cephService.uploadCeph(cephRequest);
                if (cephResponse.isOk()) {
                    //ceph上传成功后,添加到返回参数里
                    listFile.add(new FileInfo(cephRequest.getOriginFileName(),cephResponse.getFileUrl()));
                } else {
                    res.setMsg(cephResponse.getErrorMsg());
                    return res;
                }
            }
            //上传成功的标识
            if (listFile.size() > 0) {
                res.setCode(200);
                res.setMsg("ok");
            }
        } catch (Exception ex) {
            res.setCode(500);
            res.setMsg("上传失败,请稍后重试!" + ex.getMessage());
        } finally {
            res.setData(listFile);
        }
        return res;

实体类

@Data
public class CephRequest {
    /**
     * 要保存的文件名称
     */
    public String fileName;
    /**
     * 要上传的文件流
     */
    public InputStream fileStream;
    /**
     * 设置文件内容的类型
     */
    public String contextType;
    /**
     * 要保存的天数,不传默认是365*3
     */
    public int maxDays;
    /**
     * 块名称,不传用默认的
     */
    public String blockName;
    /**
     * 原始文件名.
     */
    public String originFileName;
}

然后是ceph的service层


@Service
public class CephService {
    /**
     * CEPH服务地址
     */
    public static String SERVICE_URL = "";

    /**
     * 块名称,可以自定义修改
     */
    public static String BLOCK_NAME = "";
    /**
     * ceph key
     */
    public static String ACCESS_KEY = "";
    /**
     * ceph密钥
     */
    public static String SECRET_KEY = "";
    /**
     * CEPH客户端
     */
    private AmazonS3Client s3client = null;
    /**
     * oss存储管理类
     */
    private final ICephManager cephManager;

    public CephService(ICephManager cephManager) {
        this.cephManager = cephManager;
    }

    public CephResponse uploadCeph(CephRequest param) {
        CephResponse res = new CephResponse();
        res.setOk(false);
        try {
            // 一、初始化ceph客户端
            AWSCredentials credentials = new BasicAWSCredentials(ACCESS_KEY, SECRET_KEY);
            ClientConfiguration clientCfg = new ClientConfiguration();
            clientCfg.setProtocol(Protocol.HTTP);
            s3client = new AmazonS3Client(credentials, clientCfg);
            //设置存储的服务器
            s3client.setEndpoint(SERVICE_URL);
            s3client.setS3ClientOptions(new S3ClientOptions().withPathStyleAccess(true));

            //二、上传文件
            InputStream input = param.getFileStream();
            //参数验证
            if ("".equals(param.getFileName())) {
                res.setErrorMsg("需要文件名参数");
                return res;
            } else if (input == null) {
                res.setErrorMsg("未获取到文件流");
                return res;
            } else if (param.getMaxDays() <= 0) {
                res.setErrorMsg("有效期必须大于0");
                return res;
            }
            String bucket = BLOCK_NAME;
            if (!"".equals(param.getBlockName())) {
                bucket = param.getBlockName();
            }
            // 1、先上传文件
            ObjectMetadata meta = new ObjectMetadata();
            meta.setContentLength(input.available());
            // 这里如果有请求头就设置一下,没有就不设置
            if (!StringUtils.isEmpty(param.getContextType())) {
                meta.setContentType(param.getContextType());
                System.out.println(param.getContextType());
            }
            //第二个参数可以修改为目录+文件名
            s3client.putObject(bucket, param.getFileName(), input, meta);
            //2、生成文件的外链
            GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucket, param.getFileName());
            Calendar nowTime = Calendar.getInstance();
            nowTime.add(Calendar.MINUTE, 60 * 24 * param.getMaxDays());
            request.setExpiration(nowTime.getTime());

            URL url = s3client.generatePresignedUrl(request);
            //替换文件路径 换为最后项目需要使用的路径
            //是否需要这个代码看你的业务
            res.setFileUrl(url.toString().replace("http://.com/", "https:///"));
            if ("".equals(res.getFileUrl())) {
                res.setErrorMsg("ceph上传文件失败");
                return res;
            }

            //3、保存到DB中.
            //TODO 如果保存到DB异常怎么办?
            try {
                SysCephfile file = new SysCephfile();
                file.setCreateTime(LocalDateTime.now());
                file.setBlockName(BLOCK_NAME);
                file.setExpireTime(LocalDateTime.now().plusDays(param.getMaxDays()));
                file.setOriginFileName(param.getOriginFileName());
                file.setCephKey(param.getFileName());
                // param.getContextType()
                file.setUrl(res.getFileUrl());
                file.setFileId(0L);
                this.cephManager.saveCephFile(file);
            } catch (Exception ex) {
                //根据SysCephfile的信息去删除ceph中的图片
                //s3client.deleteObject();
                LogUtil.error(ex, "保存oss存储对象异常");
            }

            res.setOk(true);
            res.setErrorMsg("");
        } catch (Exception ex) {
            res.setErrorMsg("上传ceph异常," + ex.getMessage() + ex.getStackTrace());
        } finally {
            return res;
        }
    }

}

实体类

@Data
@EqualsAndHashCode(callSuper = false)
@TableName("sys_cephfile")
public class SysCephfile implements Serializable {

private static final long serialVersionUID = 1L;

            /**
            * 自增长主键
            */
            @TableId(value = "id", type = IdType.AUTO)
    @FieldName("自增长主键")
    private Long id;

            /**
            * 具体存储的blockname
            */
    @FieldName("具体存储的blockname")
    private String blockName;

            /**
            * 创建时间
            */
    @FieldName("创建时间")
    private LocalDateTime createTime;

            /**
            * 过期时间
            */
    @FieldName("过期时间")
    private LocalDateTime expireTime;

            /**
            * 原始文件名
            */
    @FieldName("原始文件名")
    private String originFileName;

            /**
            * 传ceph的key(对外)
            */
    @FieldName("传ceph的key(对外)")
    private String cephKey;

            /**
            * 上传后的url
            */
    @FieldName("上传后的url")
    private String url;

            /**
            * 具体归属的档案ID,是0就是没保存的.
            */
    @FieldName("具体归属的档案ID,是0就是没保存的.")
    private Long fileId;


}

最后的mapper层使用的是mybatisplus,没有任何代码,纯CRUD,就不贴出了

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

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

相关文章

1.浮动 ( float )

学习目标&#xff1a; 1.1 传统网页布局的三种方式 网页布局的本质——用 CSS来摆放盒子&#xff0c;把盒子摆放到相应位置 CSS提供了三种传统布局方式(简单说,就是盒子如何进行排列顺序) : ●普通流(标准流) ●浮动 ●定位 1.2 标准流(普通流/文档流) 所谓的标准流&#xf…

Go基础知识学习

文章目录 介绍背景和起源特点和优势 安装和设置下载和安装Go语言配置Go环境变量GOROOT和GOPATHGOPROXY 编辑器、IDE等工具Hello, World!基本结构和数据类型关键字标识符变量常量基本数据类型类型转换 控制流程数组和切片数组切片 函数定义和调用函数函数参数和返回值 结构体和方…

Jmeter学习笔记

Jmeter学习笔记 1、安装 1.1、下载 官方地址&#xff1a;https://archive.apache.org/dist/jmeter/binaries/ 1.2、环境变量 此电脑 --> 属性 --> 高级系统设置 --> 环境变量 设置JMETER_HOME&#xff0c;值为jmeter的路径D:\apache-jmeter-4.0 设置Path&…

【设计模式】第十四章:策略模式详解及应用案例

系列文章 【设计模式】七大设计原则 【设计模式】第一章&#xff1a;单例模式 【设计模式】第二章&#xff1a;工厂模式 【设计模式】第三章&#xff1a;建造者模式 【设计模式】第四章&#xff1a;原型模式 【设计模式】第五章&#xff1a;适配器模式 【设计模式】第六章&…

scanpy sc.pp.normalize_per_cell bug

今天遇到一个很奇怪的bug, 当今天跑covid_atlas数据集的时候&#xff0c;在123服务器总是报错&#xff0c;但是我记得在122服务器上是跑过没问题的 最终的测试结果如下 import scanpy as sc import numpy as np from QUEST import QUEST from QUEST.utils import get_free_…

【MySQL】简介 MySQL 事务

一、事务 ACID 原则 什么是事务呢&#xff1f;事务通常是一个或一组 SQL 组成的&#xff0c;组成一个事务的 SQL 一般都是一个业务操作&#xff0c;例如下单操作&#xff1a;【扣库存数量、增加订单详情记录、插入物流信息】&#xff0c;这一组 SQL 就可以组成一个事务。 而数据…

推特开始限制用户可以阅读的推文数量

推特正在限制用户可以阅读的推文数量&#xff0c;因为埃隆马斯克 (Elon Musk) 拥有的服务长期中断&#xff0c;阻碍了用户跟踪新帖子的能力。 在一条推文中&#xff0c;马斯克详细介绍了修改后的使用配额。 经过验证的帐户持有者每天最多可以浏览 6000 个帖子&#xff0c;而未…

docker的联合文件系统 UnionFS《深入docker底层原理》

介绍 docker最大的贡献就是定义了容器镜像的分层的存储格式&#xff0c;docker镜像技术的基础是联合文件系统(UnionFS)&#xff0c;其文件系统是分层的。这样既可以充分利用共享层&#xff0c;又可以减少存储空间占用。 联合挂载系统的工作原理&#xff1a;读&#xff1a;如果…

结束 guard else 只能使用 return ?

Guard 语句 A guard statement is used to transfer program control out of a scope if one or more conditions aren’t met. 如果一个或者多个条件不成立&#xff0c;可用 guard 语句来退出当前作用域 (guard所在的作用域)。 Statement Form 语句格式 guard condition1, co…

软件设计模式与体系结构-设计模式-行为型软件设计模式-中介者模式

三、中介者模式&#xff08;迪米特法则&#xff09; 概念 模式动机&#xff1a; 为了减少对象两之间复杂的引用关系&#xff0c;使之成为一个松耦合的系统&#xff0c;需要适用中介者模式 定义&#xff1a; 用一个中介对象来封装一系列的对象交互&#xff0c;中介者使各对象…

从0开始,手写Redis

说在前面 从0开始&#xff0c;手写一个Redis的学习价值在于&#xff1a; 可以深入地理解Redis的内部机制和原理&#xff0c;Redis可谓是面试的绝对重点和难点从而更好地掌握Redis的使用和优化帮助你提高编程能力和解决问题的能力手写一个Redis可以作为一个优质的简历轮子项目…

LVS和keepalived群集

LVS和keepalived群集 一、keepalived概述&#xff1a;1.keepalived服务的重要功能&#xff1a;2.高可用故障切换转移原理&#xff1a;3.keepalived实现原理剖析&#xff1a;4.keepalived体系主要模块及作用&#xff1a;5.keepalived的工作原理&#xff1a; 二、部署LVS和keepal…

Android JetPack深入分析DataBinding源码

前言 数据绑定库是一种支持库&#xff0c;借助该库&#xff0c;您可以使用声明性格式&#xff08;而非程序化地&#xff09;将布局中的界面组件绑定到应用中的数据源。 DataBinding支持双向绑定&#xff0c;数据变化的时候界面跟着变化&#xff0c;界面变化也同步给数据&…

华为6605AC控制器大型组网wlan pool技术应用(自动漫游)

AC命令行配置&#xff1a; dis current-configuration set memory-usage threshold 0 ssl renegotiation-rate 1 vlan batch 100 vlan pool vlan_pool_test vlan 11 to 12 diffserv domain default radius-server template default free-rule-template name default_free_rule …

win10通过conda安装pytorch gpu

1. 安装anaconda 到官网下载最新版的anaconda&#xff0c;下载对应的windows版本&#xff0c;地址&#xff1a;anaconda官网 下载后直接安装&#xff0c;安装完成后配置环境变量&#xff0c;具体可以百度anaconda安装说明。安装完成后&#xff0c;打开cmd&#xff0c;输入conda…

Leetcode - 352周赛详解

一&#xff0c;最长奇偶子数组 看题可知可以使用暴力求解&#xff0c;从头开始遍历数组&#xff0c;另设一个变量来记录满足条件的子数组长度的最大值&#xff0c;将该变量不断与新得到的子数组长度比较&#xff0c;最终得到子数组长度的最大值。但是这样依次遍历的话&#xff…

git 分支管理

目录 一. 理解分支 1. master分支 二. 分支的&#xff08;创建&#xff0c;切换、合并、删除&#xff09; 1.查看分支 2.创建分支 3. 切换分支 4. 合并分支 5. 删除分支 三. 合并冲突 四. 合并模式 五. bug 分支 六. 强制删除分支 一. 理解分支 1. master分支 在我们的…

省电,AMD 提交 Linux 新技术 FreeSync Panel Replay

导读AMD 近日发布了一系列补丁&#xff0c;为其 AMDGPU Linux 内核显卡驱动程序的显示代码 “DC” 添加了一个新功能&#xff1a;FreeSync Panel Replay。这个功能针对搭载 DCN v3.1.4 显示块或更新版本的 AMD Ryzen 笔记本电脑&#xff0c;可以在屏幕内容不变时降低功耗。 目…

关于https的加密流程简介(图解)

目录 对称加密&#xff1a; 非对称加密 对称加密&#xff1a; 在网络发展的初阶&#xff0c;为了保护数据安全&#xff0c;防止黑客攻击&#xff0c;我们发明了对称加密 即一把秘钥&#xff0c;客户端和服务器通过这把钥匙对数据进行加密/解密 理想情况下&#xff0c;只要没…

第163天:应急响应-后门攻击检测指南Rookit内存马权限维持WINLinux

知识点 #知识点 -网页篡改与后门攻击防范应对指南 主要需了解&#xff1a;异常特征&#xff0c;处置流程&#xff0c;分析报告等 主要需了解&#xff1a;日志存储&#xff0c;Webshell检测&#xff0c;分析思路等 掌握&#xff1a; 中间件日志存储&#xff0c;日志格式内容介绍…