【极数系列】Flink集成KafkaSink 实时输出数据(11)

文章目录

  • 01 引言
  • 02 连接器依赖
    • 2.1 kafka连接器依赖
    • 2.2 base基础依赖
  • 03 使用方法
  • 04 序列化器
  • 05 指标监控
  • 06 项目源码实战
    • 6.1 包结构
    • 6.2 pom.xml依赖
    • 6.3 配置文件
    • 6.4 创建sink作业

01 引言

KafkaSink 可将数据流写入一个或多个 Kafka topic
实战源码地址,一键下载可用:https://gitee.com/shawsongyue/aurora.git
模块:aurora_flink_connector_kafka
主类:KafkaSinkStreamingJob

02 连接器依赖

2.1 kafka连接器依赖

        <!--kafka依赖 start-->
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-connector-kafka</artifactId>
            <version>3.0.2-1.18</version>
        </dependency>
        <!--kafka依赖 end-->

2.2 base基础依赖

     若是不引入该依赖,项目启动直接报错:Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/flink/connector/base/source/reader/RecordEmitter
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-connector-base</artifactId>
            <version>1.18.0</version>
        </dependency>

03 使用方法

Kafka sink 提供了构建类来创建 KafkaSink 的实例

DataStream<String> stream = ...;
        
KafkaSink<String> sink = KafkaSink.<String>builder()
        .setBootstrapServers(brokers)
        .setRecordSerializer(KafkaRecordSerializationSchema.builder()
            .setTopic("topic-name")
            .setValueSerializationSchema(new SimpleStringSchema())
            .build()
        )
        .setDeliveryGuarantee(DeliveryGuarantee.AT_LEAST_ONCE)
        .build();
        
stream.sinkTo(sink);

以下属性在构建 KafkaSink 时是必须指定的:
Bootstrap servers, setBootstrapServers(String)
消息序列化器(Serializer, setRecordSerializer(KafkaRecordSerializationSchema)
如果使用DeliveryGuarantee.EXACTLY_ONCE 的语义保证,则需要使用 setTransactionalIdPrefix(String)

04 序列化器

  1. 构建时需要提供 KafkaRecordSerializationSchema 来将输入数据转换为 Kafka 的 ProducerRecord。Flink 提供了 schema 构建器 以提供一些通用的组件,例如消息键(key)/消息体(value)序列化、topic 选择、消息分区,同样也可以通过实现对应的接口来进行更丰富的控制。

  2. 其中消息体(value)序列化方法和 topic 的选择方法是必须指定的,此外也可以通过 setKafkaKeySerializer(Serializer)setKafkaValueSerializer(Serializer) 来使用 Kafka 提供而非 Flink 提供的序列化器

KafkaRecordSerializationSchema.builder()
    .setTopicSelector((element) -> {<your-topic-selection-logic>})
    .setValueSerializationSchema(new SimpleStringSchema())
    .setKeySerializationSchema(new SimpleStringSchema())
    .setPartitioner(new FlinkFixedPartitioner())
    .build();

05 容错恢复

`KafkaSink` 总共支持三种不同的语义保证(`DeliveryGuarantee`)。对于 `DeliveryGuarantee.AT_LEAST_ONCE` 和 `DeliveryGuarantee.EXACTLY_ONCE`,Flink checkpoint 必须启用。默认情况下 `KafkaSink` 使用 `DeliveryGuarantee.NONE`。 以下是对不同语义保证的解释:
  • DeliveryGuarantee.NONE 不提供任何保证:消息有可能会因 Kafka broker 的原因发生丢失或因 Flink 的故障发生重复。
  • DeliveryGuarantee.AT_LEAST_ONCE: sink 在 checkpoint 时会等待 Kafka 缓冲区中的数据全部被 Kafka producer 确认。消息不会因 Kafka broker 端发生的事件而丢失,但可能会在 Flink 重启时重复,因为 Flink 会重新处理旧数据。
  • DeliveryGuarantee.EXACTLY_ONCE: 该模式下,Kafka sink 会将所有数据通过在 checkpoint 时提交的事务写入。因此,如果 consumer 只读取已提交的数据(参见 Kafka consumer 配置 isolation.level),在 Flink 发生重启时不会发生数据重复。然而这会使数据在 checkpoint 完成时才会可见,因此请按需调整 checkpoint 的间隔。请确认事务 ID 的前缀(transactionIdPrefix)对不同的应用是唯一的,以保证不同作业的事务 不会互相影响!此外,强烈建议将 Kafka 的事务超时时间调整至远大于 checkpoint 最大间隔 + 最大重启时间,否则 Kafka 对未提交事务的过期处理会导致数据丢失。

05 指标监控

Kafka sink 会在不同的范围(Scope)中汇报下列指标。

范围指标用户变量描述类型
算子currentSendTimen/a发送最近一条数据的耗时。该指标反映最后一条数据的瞬时值。Gauge

06 项目源码实战

6.1 包结构

在这里插入图片描述

6.2 pom.xml依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.xsy</groupId>
    <artifactId>aurora_flink_connector_kafka</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!--属性设置-->
    <properties>
        <!--java_JDK版本-->
        <java.version>11</java.version>
        <!--maven打包插件-->
        <maven.plugin.version>3.8.1</maven.plugin.version>
        <!--编译编码UTF-8-->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <!--输出报告编码UTF-8-->
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <!--json数据格式处理工具-->
        <fastjson.version>1.2.75</fastjson.version>
        <!--log4j版本-->
        <log4j.version>2.17.1</log4j.version>
        <!--flink版本-->
        <flink.version>1.18.0</flink.version>
        <!--scala版本-->
        <scala.binary.version>2.11</scala.binary.version>
    </properties>

    <!--通用依赖-->
    <dependencies>

        <!-- json -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson.version}</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.flink/flink-java -->
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-java</artifactId>
            <version>${flink.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-streaming-scala_2.12</artifactId>
            <version>${flink.version}</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.flink/flink-clients -->
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-clients</artifactId>
            <version>${flink.version}</version>
        </dependency>


        <!--================================集成外部依赖==========================================-->
        <!--集成日志框架 start-->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>${log4j.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>${log4j.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>${log4j.version}</version>
        </dependency>

        <!--集成日志框架 end-->

        <!--kafka依赖 start-->
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-connector-kafka</artifactId>
            <version>3.0.2-1.18</version>
        </dependency>
        <!--kafka依赖 end-->

        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-connector-base</artifactId>
            <version>1.18.0</version>
        </dependency>
    </dependencies>

    <!--编译打包-->
    <build>
        <finalName>${project.name}</finalName>
        <!--资源文件打包-->
        <resources>
            <resource>
                <directory>src/main/resources</directory>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
        </resources>

        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.1.1</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <artifactSet>
                                <excludes>
                                    <exclude>org.apache.flink:force-shading</exclude>
                                    <exclude>org.google.code.flindbugs:jar305</exclude>
                                    <exclude>org.slf4j:*</exclude>
                                    <excluder>org.apache.logging.log4j:*</excluder>
                                </excludes>
                            </artifactSet>
                            <filters>
                                <filter>
                                    <artifact>*:*</artifact>
                                    <excludes>
                                        <exclude>META-INF/*.SF</exclude>
                                        <exclude>META-INF/*.DSA</exclude>
                                        <exclude>META-INF/*.RSA</exclude>
                                    </excludes>
                                </filter>
                            </filters>
                            <transformers>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>org.aurora.KafkaStreamingJob</mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>

        <!--插件统一管理-->
        <pluginManagement>
            <plugins>
                <!--maven打包插件-->
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <version>${spring.boot.version}</version>
                    <configuration>
                        <fork>true</fork>
                        <finalName>${project.build.finalName}</finalName>
                    </configuration>
                    <executions>
                        <execution>
                            <goals>
                                <goal>repackage</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>

                <!--编译打包插件-->
                <plugin>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>${maven.plugin.version}</version>
                    <configuration>
                        <source>${java.version}</source>
                        <target>${java.version}</target>
                        <encoding>UTF-8</encoding>
                        <compilerArgs>
                            <arg>-parameters</arg>
                        </compilerArgs>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>

    <!--配置Maven项目中需要使用的远程仓库-->
    <repositories>
        <repository>
            <id>aliyun-repos</id>
            <url>https://maven.aliyun.com/nexus/content/groups/public/</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

    <!--用来配置maven插件的远程仓库-->
    <pluginRepositories>
        <pluginRepository>
            <id>aliyun-plugin</id>
            <url>https://maven.aliyun.com/nexus/content/groups/public/</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>

</project>

6.3 配置文件

(1)application.properties

#kafka集群地址
kafka.bootstrapServers=localhost:9092
#kafka主题
kafka.topic=topic_a
#kafka消费者组
kafka.group=aurora_group

(2)log4j2.properties

rootLogger.level=INFO
rootLogger.appenderRef.console.ref=ConsoleAppender
appender.console.name=ConsoleAppender
appender.console.type=CONSOLE
appender.console.layout.type=PatternLayout
appender.console.layout.pattern=%d{HH:mm:ss,SSS} %-5p %-60c %x - %m%n
log.file=D:\\tmprootLogger.level=INFO
rootLogger.appenderRef.console.ref=ConsoleAppender
appender.console.name=ConsoleAppender
appender.console.type=CONSOLE
appender.console.layout.type=PatternLayout
appender.console.layout.pattern=%d{HH:mm:ss,SSS} %-5p %-60c %x - %m%n
log.file=D:\\tmp

6.4 创建sink作业

package com.aurora;

import org.apache.flink.api.common.eventtime.WatermarkStrategy;
import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.api.common.serialization.SimpleStringSchema;
import org.apache.flink.api.java.utils.ParameterTool;
import org.apache.flink.connector.base.DeliveryGuarantee;
import org.apache.flink.connector.kafka.sink.KafkaRecordSerializationSchema;
import org.apache.flink.connector.kafka.sink.KafkaSink;
import org.apache.flink.connector.kafka.source.KafkaSource;
import org.apache.flink.connector.kafka.source.KafkaSourceBuilder;
import org.apache.flink.connector.kafka.source.enumerator.initializer.OffsetsInitializer;
import org.apache.flink.runtime.state.StateBackend;
import org.apache.flink.runtime.state.filesystem.FsStateBackend;
import org.apache.flink.streaming.api.CheckpointingMode;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.CheckpointConfig;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.util.Collector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;

/**
 * @author 浅夏的猫
 * @description kafka 连接器使用demo作业
 * @datetime 22:21 2024/2/1
 */
public class KafkaSinkStreamingJob {

    private static final Logger logger = LoggerFactory.getLogger(KafkaSinkStreamingJob.class);

    public static void main(String[] args) throws Exception {

        //===============1.获取参数==============================
        //定义文件路径
        String propertiesFilePath = "E:\\project\\aurora_dev\\aurora_flink_connector_kafka\\src\\main\\resources\\application.properties";
        //方式一:直接使用内置工具类
        ParameterTool paramsMap = ParameterTool.fromPropertiesFile(propertiesFilePath);

        //================2.初始化kafka参数==============================
        String bootstrapServers = paramsMap.get("kafka.bootstrapServers");
        String topic = paramsMap.get("kafka.topic");

        KafkaSink<String> sink = KafkaSink.<String>builder()
                //设置kafka地址
                .setBootstrapServers(bootstrapServers)
                //设置消息序列号方式
                .setRecordSerializer(KafkaRecordSerializationSchema.builder()
                        .setTopic(topic)
                        .setValueSerializationSchema(new SimpleStringSchema())
                        .build()
                )
                //至少一次
                .setDeliveryGuarantee(DeliveryGuarantee.AT_LEAST_ONCE)
                .build();


        //=================4.创建Flink运行环境=================
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        ArrayList<String> listData = new ArrayList<>();
        listData.add("test");
        listData.add("java");
        listData.add("c++");
        DataStreamSource<String> dataStreamSource = env.fromCollection(listData);

        //=================5.数据简单处理======================
        SingleOutputStreamOperator<String> flatMap = dataStreamSource.flatMap(new FlatMapFunction<String, String>() {
            @Override
            public void flatMap(String record, Collector<String> collector) throws Exception {
                logger.info("正在处理kafka数据:{}", record);
                collector.collect(record);
            }
        });

        //数据输出算子
        flatMap.sinkTo(sink);

        //=================6.启动服务=========================================
        //开启flink的checkpoint功能:每隔1000ms启动一个检查点(设置checkpoint的声明周期)
        env.enableCheckpointing(1000);
        //checkpoint高级选项设置
        //设置checkpoint的模式为exactly-once(这也是默认值)
        env.getCheckpointConfig().setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE);
        //确保检查点之间至少有500ms间隔(即checkpoint的最小间隔)
        env.getCheckpointConfig().setMinPauseBetweenCheckpoints(500);
        //确保检查必须在1min之内完成,否则就会被丢弃掉(即checkpoint的超时时间)
        env.getCheckpointConfig().setCheckpointTimeout(60000);
        //同一时间只允许操作一个检查点
        env.getCheckpointConfig().setMaxConcurrentCheckpoints(1);
        //程序即使被cancel后,也会保留checkpoint数据,以便根据实际需要恢复到指定的checkpoint
        env.getCheckpointConfig().enableExternalizedCheckpoints(CheckpointConfig.ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION);
        //设置statebackend,指定state和checkpoint的数据存储位置(checkpoint的数据必须得有一个可以持久化存储的地方)
        env.getCheckpointConfig().setCheckpointStorage("file:///E:/flink/checkPoint");
        env.execute();
    }

}

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

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

相关文章

Go语言安全编码:crypto/sha1库全面解析

Go语言安全编码&#xff1a;crypto/sha1库全面解析 简介SHA-1基础原理和特点SHA-1与其他哈希算法的比较代码示例&#xff1a;基本的SHA-1哈希生成 使用crypto/sha1处理数据处理字符串和文件的SHA-1哈希代码示例&#xff1a;为文件生成SHA-1哈希 常见错误和最佳实践 在实际项目中…

C++ PE文件信息解析

尝试解析PE文件结构, 于是编写了此PE信息助手类, 暂时完成如下信息解析 1.导出表信息(Dll模块, 函数) 2.导入表信息(Dll模块, 函数) 3.资源表信息(字符串表, 版本信息, 清单信息) CPEHelper.h #pragma once// // brief: PE文件解析助手类 // copyright: Copyright 2024 Flame…

Linux------命令行参数

目录 前言 一、main函数的参数 二、命令行控制实现计算器 三、实现touch指令 前言 当我们在命令行输入 ls -al &#xff0c;可以查看当前文件夹下所有文件的信息&#xff0c;还有其他的如rm&#xff0c;touch等指令&#xff0c;都可以帮我们完成相应的操作。 其实运行这些…

2024-02-06 TCP/UDP work

1. 画出TCP三次握手和四次挥手的示意图&#xff0c;并且总结TCP和UDP的区别 三次握手&#xff1a; 4次挥手&#xff1a; tcp/udp区别 TCP 1. 稳定&#xff0c;提供面向连接的&#xff0c;可靠的数据传输服务 2. 传输过程中&#xff0c;数据无误、数据无丢失、数据无失序、…

python+PyQt5实现指示灯检查

UI: 源代码&#xff1a; # -*- coding: utf-8 -*-# Form implementation generated from reading ui file CheckImageWinFrm.ui # # Created by: PyQt5 UI code generator 5.15.2 # # WARNING: Any manual changes made to this file will be lost when pyuic5 is # run again…

企业邮箱是什么?企业邮箱百科

本文将为大家讲解&#xff1a;1、企业邮箱的定义&#xff1b;2、企业邮箱的主要功能特点&#xff1b;3、企业邮箱如何选择和部署&#xff1b;4、企业邮箱的运营与维护&#xff1b;5、企业邮箱在实际工作中的应用与挑战&#xff1b;6、2024年最新五大企业邮箱盘点   下面提到的…

基础面试题整理6之Redis

1.Redis的应用场景 Redis支持类型&#xff1a;String、hash、set、zset、list String类型 hash类型 set类型 zset类型 list类型 一般用作缓存&#xff0c;例如 如何同时操作同一功能 2.redis是单线程 Redis服务端(数据操作)是单线程&#xff0c;所以Redis是并发安全的,因…

C语言的起源

1940年代&#xff0c;最早的开始&#xff0c;编程语言是机器语言&#xff0c;用0/1表示的、计算机能直接识别和执行的一种机器指令的集合。最早的编程方式&#xff0c;就是给纸带打孔或者卡片机打孔。机器语言直接与硬件沟通&#xff0c;极具针对性&#xff0c;但是非常难于理解…

解密 ARMS 持续剖析:如何用一个全新视角洞察应用的性能瓶颈?

作者&#xff1a;饶子昊、杨龙 应用复杂度提升&#xff0c;根因定位困难重重 随着软件技术发展迭代&#xff0c;很多企业软件系统也逐步从单体应用向云原生微服务架构演进&#xff0c;一方面让应用实现高并发、易扩展、开发敏捷度高等效果&#xff0c;但另外一方面也让软件应…

【分享】如何运用数字I/O来保护继电器

1.简述 在开关系统中&#xff0c;短路或者是开路的情况下&#xff0c;由于存在着额外的电流或者是电压&#xff0c;继电器往往会过载。所有的继电器都有一个最大的承载电流和热切换功率&#xff0c;如果超出了这个范围&#xff0c;会增加继电器焊接在一起的风险&#xff0c;从…

7.electron之渲染线程发送事件,主进程监听事件

如果可以实现记得点赞分享&#xff0c;谢谢老铁&#xff5e; Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 Electron 将 Chromium 和 Node.js 嵌入到了一个二进制文件中&#xff0c;因此它允许你仅需一个代码仓库&#xff0c;就可以撰写支持 Windows、…

蓝桥杯省赛无忧 课件92 行列式

01 什么是行列式 02 行列式的性质 03 高斯消元求行列式

vue+vite项目,动态导入静态资源的几种方式

博主的桌面工具软件已经正式开发&#xff0c;获取方式&#xff1a; 可以关注我的小程序【中二少年工具箱】获取。&#xff08;若小程序更新有延迟&#xff0c;可先收藏小程序&#xff09; 通过下载链接 百度网盘: 链接&#xff1a;https://pan.baidu.com/s/15zDnSoEzJGSZLjpD…

kafka 文件存储机制

文章目录 1. 思考四个问题&#xff1a;1.1 topic中partition存储分布&#xff1a;1.2 partiton中文件存储方式&#xff1a;1.3 partiton中segment文件存储结构&#xff1a;1.4 在partition中如何通过offset查找message: 2. kafka日志存储参数配置 Topic是逻辑上的概念&#xff…

Damn Small Linux 停更16年后,2024 回归更新

Damn Small Linux(DSL) 发行版释出了最新的 2024 版本&#xff0c;并重新定义了什么叫“Damn Small”。 DSL 诞生于 2005 年&#xff0c;原本是尝试提供一个 50MB 大小的 LiveCD&#xff0c;2008 年开发停滞。 2024 年原作者 John Andrews 宣布 DSL 复活&#xff0c;在几乎所…

微软AD域替代方案,助力企业摆脱护网期间被攻击的窘境

在红蓝攻防演练&#xff08;护网行动&#xff09;中&#xff0c;AD域若被攻击成功&#xff0c;是其中一个扣分最多的一项内容。每年&#xff0c;宁盾都会接到大量AD在护网期间被攻击&#xff0c;甚至是被打穿的企业客户。过去&#xff0c;企业还会借助2FA双因子认证加强OA、Exc…

【C#】MVVM架构

示例结果展示 前提了解 MVVM是Model-View-ViewModel的缩写形式,它通常被用于WPF或Silverlight开发。 Model——可以理解为带有字段,属性的类。例如学校类,教师类,学生类等 View——可以理解为我们所看到的UI。前端界面。 View Model在View和Model之间,起到连接的作用,…

awd总结

总结&#xff1a; 由于是第一次参加AWD比赛&#xff0c;各方面经验都不足&#xff0c;在参赛的前几天也是疯狂搜集各种脚本、框架、工具等&#xff0c;同时也参考b站的视频进行学习&#xff0c;我发现就是还是实操才能更快的学习 我觉得就是我前期的准备工作不足&#xff0c;…

挑战杯 python+大数据校园卡数据分析

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于yolov5的深度学习车牌识别系统实现 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;4分工作量&#xff1a;4分创新点&#xff1a;3分 该项目较为新颖&am…

#免费 苹果M系芯片Macbook电脑MacOS使用Bash脚本写入(读写)NTFS硬盘教程

Mac电脑苹果芯片读写NTFS硬盘bash脚本 &#xff08;ntfs.sh脚本内容在本文最后面&#xff09; ntfs.sh脚本可以将Mac系统(苹果M系芯片)上的NTFS硬盘改成可读写的挂载方式&#xff0c;从而可以直接往NTFS硬盘写入数据。此脚本免费&#xff0c;使用过程中无需下载任何收费软件。…