SpringMVC 学习(八)之文件上传与下载

news2025/1/20 5:48:54

目录

1 文件上传        

2 文件下载


1 文件上传        

 SpringMVC 对文件的上传做了很好的封装,提供了两种解析器。

  • CommonsMultipartResolver:兼容性较好,可以兼容 Servlet3.0 之前的版本,但是它依赖了 commons-fileupload 这个第三方工具,所以如果使用这个,一定要添加 commons-fileupload 依赖
  • StandardServletMultipartResolver:兼容性较差,它适用于 Servlet3.0 之后的版本,它不依赖第三方工具,使用它,可以直接做文件上传

本文使用 CommonsMultipartResolver 解析器,通过上传图片进行测试

导入依赖

导入 commons-fileupload.jar 包,Maven 会自动帮我们导入它的依赖包 commons-io.jar

<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.1</version>
</dependency>

在 SpringMVC 配置文件中配置 CommonsMultipartResolver  解析器

设置 <mvc:resources> 标签访问静态资源

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 扫描指定包下的注解 -->
    <context:component-scan base-package="com.controller"/>

    <!-- 配置访问静态资源 -->
    <!-- img 必须是在 webapp 根目录下-->
    <!-- <mvc:resources> 标签将路径 /img/** 映射到 /img/ 目录 -->
    <!-- 这意味着当客户端访问 /img/** 路径时,SpringMVC会在 /img/ 目录下寻找对应的静态资源文件-->
    <mvc:resources mapping="/img/**" location="/img/"/>

    <!-- 配置注解驱动 -->
    <mvc:annotation-driven/>

    <!-- InternalResourceViewResolver 是 SpringMVC 中用于解析和渲染内部资源视图(通常是 JSP 页面)的视图解析器。
    它根据视图名称查找并加载对应的 JSP 文件,并将模型数据传递给 JSP 进行展示 -->
    <!-- 配置 JSP 解析器 -->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 配置前缀 -->
        <property name="prefix" value="/WEB-INF/pages/"/>
        <!-- 配置后缀 -->
        <property name="suffix" value=".jsp"/>
        <!-- 定义模板顺序 -->
    </bean>

    <!-- 配置文件上传数据专用的解析器 -->
    <!-- 这个bean的id必须是multipartResolver,否则上传文件会报400的错误 -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- 设置文件编码格式 -->
        <property name="defaultEncoding" value="utf-8"/>
        <!-- 设置最大上传大小 -->
        <property name="maxUploadSize" value="#{1024*1024}"/>
    </bean>
</beans>

创建上传文件的控制器

      文件上传是使用 CommonsMultipartFile 还是 MultipartFile 呢?相信大家在学习上传文件时,肯定见过这两个类。额。。。没见过,正好可以了解下。

CommonsMultipartFile 和 MultipartFile 是 Java 中用于处理 HTTP 多部分表单数据 (Multipart Form Data) 的类。

  • CommonsMultipartFile 是 Apache Commons FileUpload 库提供的一个类,用于处理文件上传操作。它提供了更多的功能和方法,例如获取文件名、文件内容、文件类型等信息,以及设置文件的存储位置等
  • MultipartFile 是 Spring Framework 提供的一个类,用于处理文件上传和下载操作。它是基于 CommonsMultipartFile 实现的,并添加了一些 Spring 特定的功能和方法,例如通过注解进行文件解析和绑定等


        在使用时,如果你使用的是 Spring Framework,建议使用 MultipartFile 类,因为它与 Spring 的其他功能集成得更好。如果你需要更多的文件上传功能或与其他框架集成,可以考虑使用 CommonsMultipartFile 类。

        MultipartFile 封装了请求数据中的文件,此时这个文件存储在内存中或临时的磁盘文件中,需要将其转存到一个合适的位置,因为请求结束后临时存储将被清空。在 MultipartFile 接口中有如下方法:

方法名返回值描述
getContentType()String获取文件内容的类型
getOriginalFilename()String获取文件的原名称
getName()String获取 form 表单中文件组件的名字
getInputStream()InputStream将文件内容以输入流的形式返回
getSize()long获取文件的大小,单位为 byte
isEmpty()boolean文件是否为空
transferTo(File dest)void将数据保存到一个目标文件中
@Controller
public class TestController {
    @RequestMapping("/upload")
    public String testUp(MultipartFile photo, Model model, HttpSession session) throws
            IOException {
        // 获取图片文件名
        // xxx.jpg
        String originalFilename = photo.getOriginalFilename();
        System.out.println(originalFilename);
        // 使用UUID给图片重命名,并且去掉UUID的四个"-"
        // UUID.randomUUID() 随机生成 36 位的字符串
        String fileName = UUID.randomUUID().toString().replaceAll("-", "");
        // f6522af8-3f9b-4d90-a51d-e153bdb06ec6
        //获取图片的后缀
        // lastIndexOf(".") 获得字符串中最后一个 "." 的下标
        // substring(startindex) 获得字符串中从 startindex 开始的子串,即 ".jpg"
        String extName = originalFilename.substring(originalFilename.lastIndexOf("."));
        // 拼接新的图片名称
        // f6522af83f9b4d90a51de153bdb06ec6 + .jpg
        String newFileName = fileName + extName;

        // 声明转存文件时,目标目录的虚拟路径(也就是浏览器访问服务器时,能够访问到这个目录的路径)
        // 该路径需要和 SpirngMVC 配置文件中 <mvc:resources> 标签设置的路径一致
        String virtualPath = "/img";

        // 通过 session 获得 servletContext 域对象
        ServletContext servletContext = session.getServletContext();
        // 获得 img 的绝对路径  D:\JavaWeb\SpringMVCTest\target\SpringMVCTest\img
        String photoPath = servletContext.getRealPath(virtualPath);
        System.out.println("photoPath = " + photoPath);
        // 若 D:\JavaWeb\SpringMVCTest\target\SpringMVCTest\img 不存在,则创建
        File file = new File(photoPath);
        if (!file.exists()) {
            file.mkdir();
        }

        // 图片最终路径
        // D:\JavaWeb\SpringMVCTest\target\SpringMVCTest\img + f6522af83f9b4d90a51de153bdb06ec6.jpg
        String finalPath = photoPath + File.separator + newFileName;
        System.out.println("finalPath=" + finalPath);
        // 调用transferTo()方法实现文件转存,photo 是图片,photo.transferTo 相当于将图片复制到 该路径
        photo.transferTo(new File(finalPath));

        // 为了让下一个页面能够将图片展示出来,将图片访问路径存入模型,把访问图片的路径准备好
        // servletContext.getContextPath() 获得上下文路径,即 /SpringMVCTest
        // /SpringMVCTest + /img/ + f6522af83f9b4d90a51de153bdb06ec6.jpg
        String picPath = servletContext.getContextPath() + "/img/" + newFileName;
        System.out.println("picPath = " + picPath);

        model.addAttribute("picPath", picPath);
        return "success";
    }
}

index.jsp

上传文件的表单中有三点需要注意:

  • 要点1:请求方式必须是 POST
  • 要点2:enctype 属性必须是 multipart/form-data, 以二进制的方式传输数据
  • 要点3:文件上传框使用 input 标签,type 属性设置为 file,name 属性值和控制器的形参一致或通过 @RequestParam 注解重命名 name 属性值(请求参数的知识)

表单中 enctype 属性的详细说明:

  • application/x-www=form-urlencoded:默认方式,只处理表单域中的 value 属性值,采用这种编码方式的表单会将表单域中的值处理成 URL 编码方式。
  • multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数中,不会对字符编码。
  • text/plain除了把空格转换为 “+” 号外,其他字符都不做编码处理,这种方式适用直接通过表单发送邮件
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <title>文件上传</title>
    </head>
    <body>
        <h1>上传演示案例</h1>
        <form action="${pageContext.request.contextPath}/upload" method="post" enctype="multipart/form-data">
            请选择上传的图片:<input type="file" name="photo"/><br/>
            <input type="submit" value="上传文件"/>
        </form>
    </body>
</html>

success.jsp

        在控制器中,使用了 model 向 request 域对象传递了数据 picPath,可以在整个请求中使用该数据 ${picPath} 或 ${requestScope.picPath} (SpringMVC 域对象的知识)。要访问静态资源,可以在 SpringMVC 配置文件中设置 <mvc:resources> 标签。

<%@ page contentType="text/html;charset=UTF-8" %>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <p>上传成功!!!</p>
        <%-- 需要在 SpringMVC 配置文件中设置 <mvc:resources> 标签访问静态文件--%>
        <img src="${picPath}" width="220" height="280" alt="找不到该图片"/>
    </body>
</html>

执行过程

上传多张图片

修改 index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <title>文件上传</title>
    </head>
    <body>
        <h1>上传演示案例</h1>
        <form action="${pageContext.request.contextPath}/upload" method="post" enctype="multipart/form-data">
            请选择上传的图片1:<input type="file" name="photos"/><br/>
            请选择上传的图片2:<input type="file" name="photos"/><br/>
            请选择上传的图片3:<input type="file" name="photos"/><br/>
            <input type="submit" value="上传文件"/>
        </form>
    </body>
</html>

修改控制器

MultipartFile photo  -->  List<MultipartFile> photos,input 标签中 name 属性值改为 photos

@Controller
public class TestController {
    @RequestMapping("/upload")
    public String testUp(List<MultipartFile> photos, Model model, HttpSession session) throws
            IOException {
        //用于存储发送至前台页面展示的路径
        List<String> filePathNames = new ArrayList<>();
        // 声明转存文件时,目标目录的虚拟路径(也就是浏览器访问服务器时,能够访问到这个目录的路径)

        String virtualPath = "/img";

        // 准备转存文件时,目标目录的路径。File对象要求这个路径是一个完整的物理路径。
        // 而这个物理路径又会因为在服务器上部署运行时所在的系统环境不同而不同,所以不能写死。
        // 需要调用servletContext对象的getRealPath()方法,将目录的虚拟路径转换为实际的物理路径
        ServletContext servletContext = session.getServletContext();
        // 获得 img 的绝对路径  D:\JavaWeb\SpringMVCTest\target\SpringMVCTest\img
        String photoPath = servletContext.getRealPath(virtualPath);
        System.out.println("photoPath = " + photoPath);
        // 若 D:\JavaWeb\SpringMVCTest\target\SpringMVCTest\img 不存在,则创建
        File file = new File(photoPath);
        if (!file.exists()) {
            file.mkdir();
        }

        for (MultipartFile photo : photos) {
            // 获取图片文件名
            // xxx.jpg
            String originalFilename = photo.getOriginalFilename();
            System.out.println(originalFilename);
            // 使用UUID给图片重命名,并且去掉UUID的四个"-"
            String fileName = UUID.randomUUID().toString().replaceAll("-", "");
            // f6522af8-3f9b-4d90-a51d-e153bdb06ec6
            //获取图片的后缀
            // lastIndexOf(".") 获得字符串中最后一个 "." 的下标
            // substring(startindex) 获得字符串中从 startindex 开始的子串,即 ".jpg"
            String extName = originalFilename.substring(originalFilename.lastIndexOf("."));
            // 拼接新的图片名称
            // f6522af83f9b4d90a51de153bdb06ec6 + .jpg
            String newFileName = fileName + extName;


            // 图片最终路径
            // D:\JavaWeb\SpringMVCTest\target\SpringMVCTest\img + f6522af83f9b4d90a51de153bdb06ec6.jpg
            String finalPath = photoPath + File.separator + newFileName;
            System.out.println("finalPath=" + finalPath);
            // 调用transferTo()方法实现文件转存,photo 是图片,photo.transferTo 相当于将图片复制到 该路径
            photo.transferTo(new File(finalPath));

            // 为了让下一个页面能够将图片展示出来,将图片访问路径存入模型,把访问图片的路径准备好
            // servletContext.getContextPath() 获得上下文路径,即 /SpringMVCTest
            // /SpringMVCTest + /img/ + f6522af83f9b4d90a51de153bdb06ec6.jpg
            String picPath = servletContext.getContextPath() + "/img/" + newFileName;
            System.out.println("picPath = " + picPath);
            filePathNames.add(picPath);
        }
        for(int i = 0;i < filePathNames.size(); i++) {
            model.addAttribute("picPath" + (i + 1), filePathNames.get(i));
        }
        return "success";
    }
}

修改 success.jsp


<%@ page contentType="text/html;charset=UTF-8" %>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <p>上传成功!!!</p>
        <%-- 需要在 SpringMVC 配置文件中设置 <mvc:resources> 标签访问静态文件--%>
        <div>
            <img src="${picPath1}" width="220" height="280" alt="找不到该图片"/>
            <img src="${picPath2}" width="220" height="280" alt="找不到该图片"/>
            <img src="${picPath3}" width="220" height="280" alt="找不到该图片"/>
        </div>
    </body>
</html>

运行结果

2 文件下载

文件下载控制器

        传统 Servlet 文件下载方式,需要在 HttpServletResponse response 中设置各种信息,而使用 SpringMVC 的 ResponseEntity 只需要将文件二进制主体、头信息以及状态码设置好即可进行文件下载,在易用性和简洁上更胜一筹。

@RequestMapping(value = "/download")
public ResponseEntity<byte[]> testResponseEntity(String picPath, HttpSession session) throws
        IOException {
    // 获取ServletContext对象
    ServletContext servletContext = session.getServletContext();
    // 获取文件所在目录及文件名
    // picPath=/SpringMVCTest/img/05bba37fc8bc4fdfb095f41a6d2c904a.jpg
    String direcfile = "/img" + picPath.substring(picPath.lastIndexOf("/"));
    // 获取文件绝对路径
    String realPath = servletContext.getRealPath(direcfile);
    System.out.println("realPath=" + realPath);
    File file = new File(realPath);
    // 创建输入流
    InputStream is = new FileInputStream(file);
    // 创建字节数组
    byte[] bytes = new byte[is.available()];
    // 将流读到字节数组中
    is.read(bytes);
    // 创建HttpHeaders对象设置响应头信息
    MultiValueMap<String, String> headers = new HttpHeaders();
    // 设置下载文件的名字
    headers.add("Content-Disposition", "attachment; filename=" + file.getName());
    // 设置响应状态码
    HttpStatus statusCode = HttpStatus.OK;
    // 创建ResponseEntity对象
    ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers, statusCode);
    //关闭输入流
    is.close();
    return responseEntity;
}

修改 success.jsp


<%@ page contentType="text/html;charset=UTF-8" %>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <p>上传成功!!!</p>
        <%-- 需要在 SpringMVC 配置文件中设置 可以访问静态文件--%>
        <div>
            <img src="${picPath}" width="220" height="280" alt="找不到该图片"/>
        </div>
        <a href="${pageContext.request.contextPath}/download?picPath=${picPath}">点击下载图片</a>
    </body>
</html>

演示过程

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

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

相关文章

Eavesdropping(窃听机制)在机器学习中的用法

1. 简单翻译 考虑一个对任务 T 和 T’ 有用的特征 F&#xff0c;它在学习 T 时很容易学习&#xff0c;但在学习 T’ 时很难学习&#xff0c;因为 T’ 以更复杂的方式使用 F。网络学习 T 将学习 F&#xff0c;但网络学习 T’ 可能不会。如果网络学习 T’ 也学习 T&#xff0c;T…

每日汇评:黄金多头拒绝在美国宏观数据发布前放弃

周三早些时候&#xff0c;金价买家再次测试两周高点 2041美元&#xff1b; 美元延续反弹&#xff0c;但疲弱的国债收益率可能限制其上涨空间&#xff1b; 由于金价等待美国数据&#xff0c;4小时图表技术面似乎具有建设性&#xff1b; 金价正在复制周二亚洲交易中的价格走势&am…

江科大stm32 定时器 TIM输出比较--学习笔记

这几天遇到输出比较相关的问题&#xff0c;于是来学习下TIM输出比较部分知识点&#xff01; 输出比较简介 CNT是计数器的值&#xff0c;CCR寄存器是捕获/ 比较寄存器 简单的讲&#xff0c;输出比较就是用来输出PWM波形。 PWM简介 占空比&#xff1a;高电平占一个周期的比例。…

VScode打开keil5软件的内容

VScode想要打开keil5软件的内容&#xff0c;需要在此引入 具体可参考&#xff1a; VS Code环境下编辑、编译、下载Keil工程代码

本届挑战赛亚军方案:面向微服务架构系统中无标注、多模态运维数据的异常检测、根因定位与可解释性分析

CheerX团队来自于南瑞研究院系统平台研发中心&#xff0c;中心主要从事NUSP电力自动化通用软件平台的关键技术研究与软件研发。 选题分析 图1 研究现状 本次CheerX团队的选题紧密贴合了目前的运维现状。实际运维中存在多种问题导致运维系统的不可用。比如故障发生时&#xff…

【Maven】Maven 基础教程(一):基础介绍、开发环境配置

Maven 基础教程&#xff08;一&#xff09;&#xff1a;基础介绍、开发环境配置 1.Maven 是什么1.1 构建1.2 依赖 2.Maven 开发环境配置2.1 下载安装2.2 指定本地仓库2.3 配置阿里云提供的镜像仓库2.4 配置基础 JDK 版本2.5 配置环境变量 1.Maven 是什么 Maven 是 Apache 软件…

Docker部署Portainer图形化管理工具

文章目录 前言1. 部署Portainer2. 本地访问Portainer3. Linux 安装cpolar4. 配置Portainer 公网访问地址5. 公网远程访问Portainer6. 固定Portainer公网地址 前言 Portainer 是一个轻量级的容器管理工具&#xff0c;可以通过 Web 界面对 Docker 容器进行管理和监控。它提供了可…

2024最后一次Java面试,java高级开发面试经验

这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…

Python Skip-Gram代码实战,Skip-Gram代码超简单讲解和步骤拆解,Word2vec代码构建思路,Skip-Gram代码实例,模板套用

1. Skip-Gram介绍 Skip-gram模型是Word2Vec模型的一种训练方法&#xff0c;它的目标是通过目标词预测上下文词。Skip-gram模型通过神经网络结构来学习每个单词的向量表示。 在Skip-gram模型中&#xff0c;每个单词被表示为一个固定维度的向量&#xff0c;该向量称为嵌入向量或词…

AStar算法(大物件寻路)

前言 A星(物件大小为一格)寻路&#xff0c;都很熟悉了吧&#xff0c;网上源码一堆&#xff0c;随便抄; 这章需要讲述 大物件的A星寻路&#xff0c;何为大物件&#xff0c;就是 比如 物件 为4个格子&#xff1b; 这样&#xff0c;原来的A星 没法直接用了&#xff0c;必须得改装…

【Java程序员面试专栏 算法思维】四 高频面试算法题:回溯算法

一轮的算法训练完成后,对相关的题目有了一个初步理解了,接下来进行专题训练,以下这些题目就是汇总的高频题目,本篇主要聊聊回溯算法,主要就是排列组合问题,所以放到一篇Blog中集中练习 题目关键字解题思路时间空间岛屿数量网格搜索分别向上下左右四个方向探索,遇到海洋…

微信小程序引入Vant插件

Vant官网&#xff1a;Vant Weapp - 轻量、可靠的小程序 UI 组件库 先查看官网的版本 新建一个package.json页面&#xff0c;代码写上&#xff1a;&#xff08;我先执行的npm安装没出package页面&#xff0c;所以先自己创建了一个才正常&#xff09; {"dependencies"…

【Spring底层原理高级进阶】基于Spring Boot和Spring WebFlux的实时推荐系统的核心:响应式编程与 WebFlux 的颠覆性变革

&#x1f389;&#x1f389;欢迎光临&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;特别推荐给大家我的最新专栏《Spring 狂野之旅&#xff1a;底层原理高级进阶》 &#x1f680…

Bicycles(变形dijkstra,动态规划思想)

Codeforces Round 918 (Div. 4) G. Bicycles G. Bicycles 题意&#xff1a; 斯拉夫的所有朋友都打算骑自行车从他们住的地方去参加一个聚会。除了斯拉维奇&#xff0c;他们都有一辆自行车。他们可以经过 n n n 个城市。他们都住在城市 1 1 1 &#xff0c;想去参加位于城市…

c++实现栈和队列类

c实现栈和队列类 栈(Stack)Stack示意图Stack.cpp 队列(queue)queue 示意图queue.cpp 栈(Stack) Stack示意图 Stack.cpp #pragma once #include "ListStu.cpp"template<typename T> class Stack { public: /* * void push(T& tDate)* 参数一 &#xff1a;…

Android和Linux的开发差异

最近开始投入Android的怀抱。说来惭愧&#xff0c;08年就听说这东西&#xff0c;当时也有同事投入去看&#xff0c;因为恶心Java&#xff0c;始终对这玩意无感&#xff0c;没想到现在不会这个嵌入式都快要没法搞了。为了不中年失业&#xff0c;所以只能回过头又来学。 首先还是…

硬盘销毁:如何彻底销毁硬盘 文件销毁 数据销毁 物料销

大家对于办公这个词并不陌生&#xff0c;大多数人都知道办公就是对公事的处理&#xff0c;不是私人的事情处理。办公中会出现很多文件&#xff0c;很多的资料&#xff0c;那么办公室的文件销毁是如何处理的呢&#xff1f; 办公文件对于办公的人来说都是非常重视的&#xff0c;…

AI学习(5):PyTorch-核心模块(Autograd):自动求导

1.介绍 在深度学习中&#xff0c;自动求导是一项核心技术&#xff0c;它使得我们能够方便地计算梯度并优化模型参数。PyTorch 提供了一个强大的自动求导模块(Autograd)&#xff0c;它可以自动计算张量的导数得出梯度信息&#xff0c;同时也支持高阶导数计算。 1.1 概念词 在学…

微服务-商城订单服务项目

文章目录 一、需求二、分析三、设计四、编码4.1 商品服务4.2 订单服务4.3 分布式事务4.4 订单超时 商品、购物车 商品服务&#xff1a; 1.全品类购物平台 SPU:Standard Product Unit 标准化产品单元。是商品信息聚合的最小单位。是一组可复用、易检索的标准化信息的集合&#x…

EMR StarRocks实战——Mysql数据实时同步到SR

文章摘抄阿里云EMR上的StarRocks实践&#xff1a;《基于实时计算Flink使用CTAS&CDAS功能同步MySQL数据至StarRocks》 前言 CTAS可以实现单表的结构和数据同步&#xff0c;CDAS可以实现整库同步或者同一库中的多表结构和数据同步。下文主要介绍如何使用Flink平台和E-MapRed…