Spring Cloud Feign MultipartFile文件上传踩坑之路(包含前端文件上传请求、后端文件保存到aliyun-oss文件服务器)

news2024/12/24 9:03:32

Spring Cloud Feign MultipartFile文件上传踩坑之路总结

一、前端文件上传

文件上传组件用的是ant-design的a-upload组件,我的界面如下所示:
在这里插入图片描述
文件上传请求API:
FileUtils.js

import axios from "axios"

const uploadApi = ({file, URL, onUploadProgress}) => {
    const formData = new FormData()
    formData.append('file', file)
    return axios.post(URL, formData, {headers:{
        'Content-type': 'multipart/form-data',
      },
      onUploadProgress // 上传进度回调函数 onUploadProgress(ev))
    }) 
}

export default uploadApi;

需要注意的只有FileUtils.js定义的uploadApi请求函数,其中URL为后端请求接口(“/imageConvert/upload”),文件上传方法必须定义为POST,在headers加入’Content-type’: ‘multipart/form-data’,后端即可用@RequestParam或者@RequestPart + MultipartFile 来接受文件。
FileUpload.vue(无关紧要,用法大致相同,看你自己需求,这里只是提供一个参考范例)

// 自定义文件上传公共函数
// e - 上传组件返回的上传实例,里面包括 file,和一些组件方法
// e.file - 上传的文件实例对象
const customUpload = e => {
    let curFile = fileList.value.filter(item => item.uid == e.file.uid)[0]
    curFile.status = 'uploading'
    uploadApi({
        file: e.file,
        URL: '/imageConvert/upload',
        // uid: 'admin',  // 需要更改为用户id,待修改
        onUploadProgress: ev => {
            // ev - axios 上传进度实例,上传过程触发多次
            // ev.loaded 当前已上传内容的大小,ev.total - 本次上传请求内容总大小
            // console.log(ev);
            const percent = (ev.loaded / ev.total) * 100;
            // 计算出上传进度,调用组件进度条方法
            e.onProgress({ percent });
        }
    })
        .then(res => {
            let curFile = fileList.value.filter(item => item.uid == e.file.uid)[0]
            curFile.response = res.data
            if(res.data.code == 400) {
                curFile.status = 'error'
                curFile['error'] = curFile.response.msg
                console.error(`文件${curFile.name}上传失败:${res.data.msg}`)
            } else {
                // 通知组件该文件上传成功
                curFile.status = 'done'
                curFile.url = res.data.data
                curFile.thumbUrl = res.data.data
                console.log(`文件${curFile.name}上传成功`, curFile.url);
            }
        })
        .catch(err => {
            let curFile = fileList.value.filter(item => item.uid == e.file.uid)[0]
            curFile.status = 'error'
            curFile['error'] = '文件传输失败'
            console.log('上传失败', err);
        })
}

二、后端处理

后端框架我这里使用的是Spring Cloud,将文件处理统一定义为一个单独模块,通过Feign为其他业务模块提供服务。

服务提供者

Controller
这里注意要在@PostMapping加入MediaType.MULTIPART_FORM_DATA_VALUEMediaType.MULTIPART_FORM_DATA_VALUE,并且参数使用@RequestPart来接受参数

@RefreshScope
@RestController
@RequestMapping("/oss/file")
public class OSSFileController {
    @Autowired
    private IOSSService ossService;

    /**
     * 文件上传,入参可以根据具体业务进行添加
     * @param file 文件
     * @return 响应结果
     */
    @PostMapping( value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public String uploadFile(@RequestPart("file") MultipartFile file, @RequestParam("storagePath") String storagePath) {
        return ossService.uploadFile(file, storagePath);
    }
}

Service(文件存储方式跟Feign没关系,可忽略)
收到文件后我们将其保存在aliyun-oss文件服务器中:
如何将文件保存在aliyun-oss具体请参考:Spring Boot 集成阿里云 OSS 进行文件存储
或者可以使用file.transferTo(File file)保存至本地

**
 * OSS服务类
 * / @Author: ZenSheep
 * / @Date: 2023/8/10 16:05
 */
@Service
public class OSSService implements IOSSService {
    @Autowired
    private OSS ossClient;

    @Autowired
    private OSSConfiguration ossConfiguration;

    /**
     * 上传文件到阿里云 OSS 服务器
     * 链接:https://help.aliyun.com/document_detail/oss/sdk/java-sdk/upload_object.html?spm=5176.docoss/user_guide/upload_object
     *
     * @param file 文件
     * @param storagePath 文件存储路径
     * @return 文件存储完整路径
     */
    @Override
    public String uploadFile(MultipartFile file, String storagePath) {
        String url = "";
        try {
            // UUID生成文件名,防止重复
            String fileName = "";
            String baseName = OSSFileUtils.getBaseName(OSSFileUtils.getBaseName(file.getOriginalFilename()));
            InputStream inputStream = file.getInputStream();
            // 创建ObjectMetadata,设置用户自定义的元数据以及HTTP头,比如内容长度,ETag等
            ObjectMetadata objectMetadata = new ObjectMetadata();
            objectMetadata.setContentLength(inputStream.available());
            objectMetadata.setCacheControl("no-cache");
            objectMetadata.setHeader("Pragma", "no-cache");
            objectMetadata.setContentType(OSSFileUtils.getcontentType(file.getOriginalFilename()));
            objectMetadata.setContentDisposition("inline;filename=" + baseName);
            fileName = storagePath + "/" + UUID.randomUUID().toString() + "/"  + file.getOriginalFilename();
            // 上传文件:调用ossClient的putObject方法完成文件上传,并返回文件名
            ossClient.putObject(ossConfiguration.getBucketName(), fileName, inputStream, objectMetadata);
            // 设置签名URL过期时间,单位为毫秒。
            Date expiration = new Date(new Date().getTime() + 3600 * 1000);
            // 生成以GET方法访问的签名URL,访客可以直接通过浏览器访问相关内容。
            url = ossClient.generatePresignedUrl(ossConfiguration.getBucketName(), fileName, expiration).toString();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return url;
    }
}

Feign

引入依赖:

        <!-- SpringCloud Openfeign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

RemoteFileService
这里同样需要注意:@PostMapping需要加入consumes = MediaType.MULTIPART_FORM_DATA_VALUE,参数传递用@RequestPart(“file”)

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.multipart.MultipartFile;

/**
 * File Feign: 提供File的远程服务
 * / @Author: ZenSheep
 * / @Date: 2023/8/14 18:48
 */
@FeignClient(name = "opentool-system", contextId="remote-file")
public interface RemoteFileService {
    @PostMapping(value = "/oss/file/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    String uploadFile(@RequestPart("file") MultipartFile file, @RequestParam("storagePath") String storagePath);
}

服务消费者

Controller

/**
 * 图像转换控制类
 * / @Author: ZenSheep
 * / @Date: 2023/8/14 18:59
 */
@RefreshScope
@RestController
@RequestMapping("/imageConvert")
public class ImageConvertController {
    @Autowired
    IImageConvertService iImageConvertService;
    @PostMapping("/upload")
    public R<?> uploadFile(@RequestPart("file") MultipartFile file) {
        return R.ok(iImageConvertService.uploadFile(file, "ImageConvert/images"));
    }
}

Service(在这里调用feign服务)

/**
 * 图像转换服务类
 * / @Author: ZenSheep
 * / @Date: 2023/8/14 18:53
 */
@Service
public class ImageConvertService implements IImageConvertService {
    @Autowired
    private RemoteFileService remoteFileService;

    @Override
    public String uploadFile(MultipartFile file, String storagePath) {
       return remoteFileService.uploadFile(file, storagePath);
    }
}

ok,到这一步我们的工作就完成了,测试一下:
在这里插入图片描述
在这里插入图片描述
可以看到我们的文件已经成功上传,并成功保存至目标服务器返回了一个文件存储url,有什么不懂的可以在评论区问我,哪里讲的不对请大佬轻喷,我也是第一次做文件传输。

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

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

相关文章

NLP序列标注问题,样本不均衡怎么解决?

【学而不思则罔&#xff0c;思而不学则殆】 1.问题 NLP序列标注问题&#xff0c;样本不均衡怎么解决&#xff1f; 2.解释 以命名实体识别&#xff08;NER&#xff09;为例&#xff0c;这个样本不均衡有两种解释&#xff1a; &#xff08;1&#xff09;实体间类别数量不均衡…

从外部访问K8s中Pod的五种方式

hostNetwork、 hostPort、 NodePort、 LoadBalancer、 Ingress 暴露Pod与Service一样&#xff0c;因为Pod就是Service的backend 1、hostNetwork&#xff1a;true 这是一种直接定义 Pod 网络的方式。 如果在 Pod 中使用 hostNetwork:true 配置&#xff0c; pod 中运行的应用程序…

[计算机入门] 设置屏幕分辨率

3.1 设置屏幕分辨率 3.1.1 屏幕分辨率介绍 屏幕分辨率&#xff0c;是指屏幕上显示的像素个数&#xff0c;它决定了显示图像的清晰度和精细度。屏幕分辨率通常以水平像素数和垂直像素数来衡量&#xff0c;例如1600 x 1200。 在相同大小的屏幕上&#xff0c;当屏幕分辨率较低时…

vue中的路由缓存和解决方案

路由缓存的原因 解决方法 推荐方案二&#xff0c;使用钩子函数beforeRouteUpdate&#xff0c;每次路由更新前执行

[自学记录06|*百人计划]Gamma矫正与线性工作流

一、前言 Gamma矫正其实也属于我前面落下的一块内容&#xff0c;打算把它补上&#xff0c;其它的没补是因为我之前写的GAMES101笔记里已经涵盖了&#xff0c;而Gamma矫正在101里面确实没提到&#xff0c;于是打算把它补上&#xff0c;这块内容并不难&#xff0c;但是想通透的理…

什么是CSS预处理器?请列举几个常见的CSS预处理器。

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ CSS预处理器是什么&#xff1f;⭐ 常见的CSS预处理器⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏是…

FPGA_学习_14_第一个自写模块的感悟和ila在线调试教程与技巧(寻找APD的击穿偏压)

前一篇博客我们提到了&#xff0c;如果要使用算法找到Vbr&#xff0c;通过寻找APD采集信号的噪声方差的剧变点去寻找Vbr是一个不错的方式。此功能的第一步是在FPGA中实现方差的计算&#xff0c;这个我们已经在上一篇博客中实现了。 继上一篇博客之后&#xff0c;感觉过了很久了…

这场大学生竞赛中,上百支队伍与合合信息用AI共克难题

目录 0 校企联合共克难题1 北京林业大学&#xff1a;文档格式转换2 浙江中医药大学&#xff1a;个性化题库3 中南林业科技大学&#xff1a;交互场景挖掘4 重庆邮电大学&#xff1a;大模型赋能智能文档5 总结 0 校企联合共克难题 近日&#xff0c;中国大学生服务外包创新创业大…

如何使用CSS实现一个响应式视频播放器?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 使用CSS实现响应式视频播放器⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏是为那些对Web开发感兴趣…

avue多选列表根据后端返回的某个值去判断是否选中;avue-curd多选回显

效果如上&#xff1a; getSiteList().then(res > {//列表数据this.siteData res.data.datathis.$nextTick(()>{this.siteData.forEach(item>{//业务条件if(item.configid&&item.configid!0&&item.configid>0){//符合条件时调用选中的方法this.$…

一文科普,买股票加杠杆是怎么回事?其利弊表现在哪?

买股票加杠杆是一种投资策略&#xff0c;通过借入资金来增加投资额度&#xff0c;进而放大投资回报。这种策略的利弊主要表现在以下几个方面。 首先&#xff0c;加杠杆的主要利表现在于放大投资回报。借入的资金可以投资更多的股票&#xff0c;当股票价格上涨时&#xff0c;投资…

XDR解决方案三

XDR未来的进化之路 精细化、智能化、个性化和场景化 当前XDR未来发展方向的问题分享了他的洞察和思考。刘庆林认为&#xff0c;精细化、智能化、个性化和场景化将是未来XDR的重要方向。 首先&#xff0c;实现精准防护的唯一路径&#xff0c;就是要从云、管、边、端、人五个维…

8.15 IO的多路复用

select的TCP客户端 poll的TCP客户端

探索网络架构的关键角色:六种常用的服务器类型

在今天的数字时代&#xff0c;服务器是支撑各种在线服务和应用的基石。不同类型的服务器在网络架构中扮演着不同的角色&#xff0c;从网页传输到电子邮件交换&#xff0c;再到文件传输和内容分发。本文将深入探讨六种最常用的服务器类型&#xff0c;解释它们的功能和重要性&…

梅林(Asuswrt-Merlin)>384.7固件自定义DDNS服务

指南https://github.com/RMerl/asuswrt-merlin.ng/wiki/DDNS-services配置文件示例https://github.com/troglobit/inadyn/tree/master/examples1 插入优盘&#xff0c;下载配置文件示例修改&#xff0c;以cloudflare为例 provider cloudflare.com {username 域名.compassword…

【后端必看】Redis 最佳实践

文章目录 1. Redis 键值设计1.1 优雅的 key 结构1.2 拒绝 BigKeyBigKey的危害如何发现BigKey①redis-cli --bigkeys② scan 扫描③第三方监控④网络监控 如何删除 BigKey 1.3 恰当的数据类型总结&#xff1a; 2. 批处理优化3. 服务器端优化-持久化配置4. 服务器端优化-慢查询优…

SpringBoot案例-员工管理-分页条件查询

根据页面原型&#xff0c;明确需求 页面原型 需求 查看接口文档 接口文档的链接如下&#xff1a; 【腾讯文档】SpringBoot案例所需文档 https://docs.qq.com/doc/DUkRiTWVaUmFVck9N 思路分析 分页条件查询就时将条件查询的结果进行分页展示&#xff0c;由于有的条件可能设…

问题:RuntimeError: Distributed package doesn‘t have NCCL built in

现象 python在windows环境下dist.init_process_group(backend, rank, world_size)处报错‘RuntimeError: Distributed package doesn’t have NCCL built in’ 原因分析 windows不支持NCCL backend 方法1 import sysif sys.platform "win32":os.environ["…

基于X86六轮差速移动机器人运动控制器设计与实现(一)软件与硬件架构

本文研究的六轮差速移动机器人 (Six-Wheeled Differential Mobile Robot &#xff0c; SWDMR) 为了满足资源站到资源站点对点的物资运输&#xff0c;对机器人的跨越障碍能力 有较高的要求。对比传统的四轮移动机器人&#xff0c;六轮移动机器人能够提供更强的驱动 力&#…

BBS项目day03、首页(前端文章布局、分类布局、标签布局)、个人站点(前后端实现)

一、首页 路由 from django.contrib import admin from django.urls import path, re_path from app01 import views from django.views.static import serve from django.conf import settingsurlpatterns [path(admin/, admin.site.urls),# 注册path(register/, views.reg…