使用Netty实现Socket网络编程

**

Netty初步讲解和认识

**

网络通信模型

Netty支持多种网络通信模型,包括传统的阻塞I/O、非阻塞I/O、多路复用I/O和异步I/O。其中,非阻塞I/O和多路复用I/O是Netty的核心特性。

  • 非阻塞I/O:Netty通过使用Java的NIO(New I/O)库,实现了非阻塞的I/O操作。这意味着当一个操作正在进行时,不会阻塞线程,线程可以继续处理其他任务。这种模型非常适合高并发的网络应用程序,可以提供更高的吞吐量和并发性能。

  • 多路复用I/O:Netty使用了Reactor模式,通过一个线程池处理多个I/O事件,提高了系统的资源利用率。Netty的多路复用I/O模型可以同时处理成千上万个连接,而每个连接只需使用一个线程,极大地减少了线程的创建和上下文切换开销。

事件处理

Netty使用了事件驱动的编程模型来处理网络操作。它提供了一系列的事件和处理器,开发者可以根据自己的需求自定义处理逻辑。

  • 事件:Netty的核心是事件,它代表了网络操作中的各种状态和动作,如连接建立、数据接收、数据发送等。Netty提供了多种类型的事件,每个事件都有对应的处理器进行处理。

  • 处理器:处理器是用于处理特定类型事件的组件,它包含了业务逻辑的实现。开发者可以通过编写自定义的处理器来实现特定的功能。处理器可以被链接成处理器链,形成一个处理事件的流水线。

通过使用事件和处理器,开发者可以实现复杂的网络应用程序,例如实现自定义的协议、数据解析、安全认证等。

协议设计

Netty提供了灵活的协议设计和实现能力,使开发者可以轻松构建自己的网络协议。

  • 编解码器:Netty提供了一系列的编解码器,用于将字节数据和Java对象相互转换。这些编解码器可以用于构建自定义协议,实现数据的序列化和反序列化。

  • 自定义协议:通过使用Netty的事件和处理器机制,开发者可以自定义网络协议。可以根据自己的需求定义协议的消息格式、数据传输方式、错误处理等。

性能优化

Netty提供了许多性能优化的功能和技术,以提高网络应用程序的性能和可扩展性。

  • 零拷贝:Netty使用了零拷贝技术,避免了数据在内存之间的复制操作,减少了CPU和内存的开销。这对于处理大量数据的高性能应用程序尤为重要。

  • 内存管理:Netty提供了高效的内存管理机制,可以有效地管理内存的分配和释放。它使用了内存池和内存复用的技术,减少了内存的分配和回收频率,提高了性能。

  • 高性能传输:Netty支持多种高性能传输协议,如TCP、UDP和Unix域套接字。开发者可以根据自己的需求选择合适的传输协议,以获得最佳的性能。

在本篇博客中,我们将学习如何使用Netty框架实现基于Socket的异步通信。Netty是一个高性能、异步事件驱动的网络应用程序框架,它简化了网络编程的复杂性,并提供了可靠的、高效的数据传输。

使用Netty实现Socket网络编程

环境准备

在开始之前,确保您已经安装了Java和Maven。然后,按照以下步骤进行操作:

  1. 创建一个Maven项目,并在pom.xml中添加以下依赖项:
<dependencies><dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.78.Final</version></dependency>
</dependencies>
  1. 在src/main/java目录下创建以下两个Java类:Server.java和Client.java。

服务端代码

在Server.java中,我们将创建一个服务端,并监听指定的端口。当客户端连接到服务器时,我们将打印出连接成功的消息,并向客户端发送一条欢迎消息。

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;public class Server {private int port;public Server(int port) {this.port = port;}public void start() throws Exception {EventLoopGroup bossGroup = new NioEventLoopGroup();EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).handler(new LoggingHandler(LogLevel.INFO)).childHandler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) throws Exception {// 这里可以流水线式加载各种Handler,以实现预期的功能ch.pipeline().addLast(new ServerHandler());}});ChannelFuture f = b.bind(port).sync();System.out.println("Server started on port " + port);f.channel().closeFuture().sync();} finally {workerGroup.shutdownGracefully();bossGroup.shutdownGracefully();}}public static void main(String[] args) throws Exception {int port = 8080;new Server(port).start();}
}

在上述代码中,我们创建了两个EventLoopGroup:bossGroup和workerGroup。bossGroup负责接受客户端的连接,而workerGroup负责处理客户端的请求。

我们使用ServerBootstrap配置和启动服务器。在childHandler方法中,我们初始化通道的处理器链,并添加了一个名为ServerHandler的处理器。

通过bind方法绑定服务器端口,并通过sync方法等待服务器启动完成。

客户端代码

在Client.java中,我们将创建一个客户端,并连接到指定的服务器。一旦连接成功,我们将向服务器发送一条消息,并打印出服务器返回的响应消息。

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;public class Client {private String host;private int port;public Client(String host, int port) {this.host = host;this.port = port;}public void start() throws Exception {EventLoopGroup group = new NioEventLoopGroup();try {Bootstrap b = new Bootstrap();b.group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast(new ClientHandler());}});ChannelFuture f = b.connect(host, port).sync();System.out.println("Connected to server: " + host + ":" + port);// 发送消息给服务器String message = "Hello, server!";f.channel().writeAndFlush(message);// 等待直到连接关闭f.channel().closeFuture().sync();} finally {group.shutdownGracefully();}}public static void main(String[] args) throws Exception {String host = "localhost";int port = 8080;new Client(host, port).start();}
}

在上述代码中,我们创建了一个EventLoopGroup,并使用Bootstrap配置和启动客户端。在handler方法中,我们初始化通道的处理器链,并添加了一个名为ClientHandler的处理器。

通过connect方法连接到服务器,并通过sync方法等待连接完成。

在连接建立后,我们向服务器发送一条消息,并使用writeAndFlush方法将其发送给服务器。

最后,我们通过调用closeFuture方法等待连接关闭。

服务端处理器

在ServerHandler.java中,我们编写一个处理器,用于处理从客户端接收到的消息并发送响应消息。

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;public class ServerHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {// 读取客户端发送的消息ByteBuf buf = (ByteBuf) msg;String message = buf.toString();System.out.println("Received message from client: " + message);// 发送响应消息给客户端String response = "Hello, client!";ByteBuf responseBuf = ctx.alloc().buffer(response.length());responseBuf.writeBytes(response.getBytes());ctx.writeAndFlush(responseBuf);}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();ctx.close();}
}

在上述代码中,我们重写了channelRead方法,用于读取客户端发送的消息。我们将接收到的消息转换为字符串,并打印出来。

然后,我们准备发送响应消息给客户端。首先,我们创建一个ByteBuf来存储响应消息的字节数据。然后,将字符串转换为字节数组,并将其写入ByteBuf中。

最后,我们使用ctx.writeAndFlush方法将响应消息发送给客户端。

客户端处理器

在ClientHandler.java中,我们编写一个处理器,用于处理从服务器接收到的响应消息。

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;public class ClientHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {// 读取服务器发送的响应消息ByteBuf buf = (ByteBuf) msg;String response = buf.toString();System.out.println("Received response from server: " + response);// 关闭连接ctx.close();}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();ctx.close();}
}

在上述代码中,我们重写了channelRead方法,用于读取服务器发送的响应消息。我们将接收到的消息转换为字符串,并打印出来。

然后,我们调用ctx.close方法关闭与服务器的连接。

运行应用程序

现在,我们已经完成了Netty的Socket网络编程示例。在终端中,分别运行Server和Client的main方法,您将看到服务器和客户端之间的通信。

这就是使用Netty实现Socket网络编程的基本过程。通过使用Netty,我们可以轻松地构建高性能的、可靠的网络应用程序。

Netty实现静态页面的网络请求基础流程:

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;public class Server {private int port;public Server(int port) {this.port = port;}public void start() throws Exception {EventLoopGroup bossGroup = new NioEventLoopGroup();EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).handler(new LoggingHandler(LogLevel.INFO)).childHandler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) throws Exception {// 这里可以流水线式加载各种Handler,以实现预期的功能ch.pipeline().{添加后续的Http处理器(见下文)}}});ChannelFuture f = b.bind(port).sync();System.out.println("Server started on port " + port);f.channel().closeFuture().sync();} finally {workerGroup.shutdownGracefully();bossGroup.shutdownGracefully();}}public static void main(String[] args) throws Exception {int port = 8080;new Server(port).start();}
}

如在这里添加一个对于页面的解决方案,创建一个新的类封装;

import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;import java.io.IOException;
import java.io.InputStream;public class PageResolver {//直接单例模式private static final PageResolver INSTANCE = new PageResolver();private PageResolver(){}public static PageResolver getInstance(){return INSTANCE;}//请求路径给进来,接着我们需要将页面拿到,然后转换成响应数据包发回去public FullHttpResponse resolveResource(String path){if(path.startsWith("/"))  {  //判断一下是不是正常的路径请求path = path.equals("/") ? "index.html" : path.substring(1);    //如果是直接请求根路径,那就默认返回index页面,否则就该返回什么路径的文件就返回什么try(InputStream stream = this.getClass().getClassLoader().getResourceAsStream(path)) {if(stream != null) {   //拿到文件输入流之后,才可以返回页面byte[] bytes = new byte[stream.available()];stream.read(bytes);return this.packet(HttpResponseStatus.OK, bytes);  //数据先读出来,然后交给下面的方法打包}} catch (IOException e){e.printStackTrace();}}//其他情况一律返回404return this.packet(HttpResponseStatus.NOT_FOUND, "404 Not Found!".getBytes());}//包装成FullHttpResponse,把状态码和数据写进去private FullHttpResponse packet(HttpResponseStatus status, byte[] data){FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status);response.content().writeBytes(data);return response;}
}

然后把流水线中加入这个Http的解码器和译码器,以及一个ChannelInboundHandlerAdapter,处理解析到的数据;

ch.pipeline().addLast(new HttpRequestDecoder())   //Http请求解码器.addLast(new HttpObjectAggregator(Integer.MAX_VALUE))  //搞一个聚合器,将内容聚合为一个FullHttpRequest,参数是最大内容长度.addLast(new ChannelInboundHandlerAdapter(){@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {FullHttpRequest request = (FullHttpRequest) msg;//请求进来了直接走解析PageResolver resolver = PageResolver.getInstance();ctx.channel().writeAndFlush(resolver.resolveResource(request.uri()));ctx.channel().close();}}).addLast(new HttpResponseEncoder());

可以在添加一个自定义组件例如:

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.EventLoopGroup;
import io.netty.handler.codec.http.FullHttpRequest;import java.util.Objects;public class ShutdownHandler extends ChannelInboundHandlerAdapter {public EventLoopGroup bootstrap;public EventLoopGroup workstrap;public ShutdownHandler(EventLoopGroup bootstrap,EventLoopGroup workstrap){this.bootstrap = bootstrap;this.workstrap = workstrap;}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {FullHttpRequest request = (FullHttpRequest) msg;String uri = request.uri();// 关闭服务if(Objects.equals(uri, "/shutdown")){ctx.channel().close();bootstrap.shutdownGracefully();workstrap.shutdownGracefully();}else{// 继续向下传递消息ctx.fireChannelRead(msg);}}
}

Server的总代码:

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import org.example.handler.PageResolver;
import org.example.handler.ShutdownHandler;public class Server {private final int port;public Server(int port) {this.port = port;}public void start() throws Exception {EventLoopGroup bossGroup = new NioEventLoopGroup();EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).handler(new LoggingHandler(LogLevel.INFO)).childHandler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) throws Exception {// 这里可以流水线式加载各种Handler,以实现预期的功能ch.pipeline().addLast(new HttpRequestDecoder())   //Http请求解码器.addLast(new HttpObjectAggregator(Integer.MAX_VALUE))  //搞一个聚合器,将内容聚合为一个FullHttpRequest,参数是最大内容长度.addLast(new ShutdownHandler(bossGroup, workerGroup)).addLast(new ChannelInboundHandlerAdapter(){@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {FullHttpRequest request = (FullHttpRequest) msg;//请求进来了直接走解析PageResolver resolver = PageResolver.getInstance();ctx.channel().writeAndFlush(resolver.resolveResource(request.uri()));ctx.channel().close();}}).addLast(new HttpResponseEncoder());}});ChannelFuture f = b.bind(port).sync();System.out.println("Server started on port " + port);f.channel().closeFuture().sync();} finally {workerGroup.shutdownGracefully();bossGroup.shutdownGracefully();}}public static void main(String[] args) throws Exception {int port = 8081;new Server(port).start();}
}

最后启动服务器,就可以访问到resource中的静态页面资源了。

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

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

相关文章

JavaScript删除数组中指定元素的五种方法有哪些

JavaScript中删除数组中指定元素的方法有多种&#xff0c;以下列出五种常见方法&#xff1a; 使用filter()方法&#xff1a;filter()方法创建一个新数组&#xff0c;新数组中的元素是通过检查指定条件后从原数组中筛选出来的。如果元素满足条件&#xff0c;它就会被包含在新数组…

buuctf[极客大挑战 2019]BabySQL--联合注入、双写过滤

目录 1、测试万能密码&#xff1a; 2、判断字段个数 3、尝试联合注入 4、尝试双写过滤 5、继续尝试列数 6、查询数据库和版本信息 7、查询表名 8、没有找到和ctf相关的内容&#xff0c;查找其他的数据库 9、查看ctf数据库中的表 10、查询Flag表中的字段名 11、查询表…

Python图像处理【17】指纹增强和细节提取

指纹增强和细节提取 0. 前言1. 形态学操作基础2. 利用形态学操作进行指纹增强3. 从增强指纹中提取特征(细节)3.1 指纹细节概念3.2 提取指纹细节 小结系列链接 0. 前言 指纹识别和验证是最古老&#xff0c;最流行和广泛使用的生物特征技术。众所周知&#xff0c;每个人都有独特…

模型\视图一般步骤:为什么经常要用“选择模型”QItemSelectionModel?

一、“使用视图”一般的步骤&#xff1a; //1.创建 模型(这里是数据模型&#xff01;) tabModelnew QSqlTableModel(this,DB);//数据表 //2.设置 视图的模型(这里是数据模型&#xff01;) ui->tableView->setModel(tabModel); 模型种类&#xff1a; QStringListModel…

SemiDrive E3 打包说明

一、 概述 本文介绍 E3 PAC 打包&#xff0c;编译器生成 bin 文件需要通过打包生成 PAC 包&#xff0c;再通过 SDToolBox 工具将 PAC 包烧写到芯片&#xff0c;PAC 包的物理载体分为 Flash、eMMC、SD&#xff0c;一个 PAC包最多支持 3 个BootPackage&#xff1b;本文主要描述打…

代码随想录算法训练营第一天| 27 移除元素 704 二分查找

目录 27 移除元素 704 二分查找 27 移除元素 快指针遍历&#xff0c;慢指针记录 class Solution { public:int removeElement(vector<int>& nums, int val) {int l 0,r 0;for(;r < nums.size();r){if(nums[r] val){}else{nums[l] nums[r];}}return l;} }; …

iOS 应用上架指南:资料填写及提交审核

摘要 本文提供了iOS新站上架资料填写及提交审核的详细指南&#xff0c;包括创建应用、资料填写-综合、资料填写-IOS App和提交审核等步骤。通过本指南&#xff0c;您将了解到如何填写正确的资料&#xff0c;并顺利通过苹果公司的审核。 引言 在开发iOS应用后&#xff0c;将其…

医院里的管家婆,如何才能把科室的设备设施管理好?

在医院的各个科室中&#xff0c;有一位不可或缺的重要角色&#xff0c;科室的“管家婆”&#xff0c;大事、小事&#xff0c;事事都归她管&#xff0c;她就是护士长。她不仅是护理工作的核心&#xff0c;更是科室设备设施的“管家婆”。管理众多设备和资产&#xff0c;确保其正…

ArcMap实现多行标注

地图标注是地图的重要组成部分&#xff0c;也是地理信息的重要表达方式​。ArcMap的符号化系统为我们添加地图标注提供了方便&#xff0c;但是有时我们却需要添加多行标注&#xff0c;今天我们一起来探索一下ArcMap中两行标注的实现方式​。 首先&#xff0c;我们右击目标图层…

Oracle-探究统计信息收集自动采样AUTO_SAMPLE_SIZE

前言&#xff1a; Oracle数据库进行统计信息收集时&#xff0c;可以通过ESTIMATE_PERCENT参数指定采样方式或者比例&#xff0c;有以下4种指定的方式 1 统计信息收集时不指定值&#xff0c;这时候ESTIMATE_PERCENT值为默认值DBMS_STATS.AUTO_SAMPLE_SIZE&#xff0c;自动采样 …

TXB2 ELISA kit—Enzo Life Sciences ELISA试剂盒

高灵敏、经过充分验证的ELISA试剂盒&#xff0c;3小时内即可得结果 血栓素A2&#xff08;TXA2&#xff09;参与血小板聚集、血管收缩和生殖功能&#xff0c;但在生理条件下的半衰期仅有37秒。血栓素B2&#xff08;TXB2&#xff09;是TXA2非酶水合的稳定产物&#xff0c;因此体内…

Kafka之集群搭建

1. 为什么要使用kafka集群 单机服务下&#xff0c;Kafka已经具备了非常高的性能。TPS能够达到百万级别。但是&#xff0c;在实际工作中使用时&#xff0c;单机搭建的Kafka会有很大的局限性。 ​ 消息太多&#xff0c;需要分开保存。Kafka是面向海量消息设计的&#xff0c;一个T…

Python专家编程系列: 8. 高级数据结构介绍

0. 标题 Python专家编程系列: 8. 高级数据结构介绍 id:4 作者: quantgalaxyoutlook.com blog: https://blog.csdn.net/quant_galaxy 欢迎交流1. 介绍 Python中&#xff0c;除了大家常用的数据结构外&#xff0c;还有几个非常好用的数据结构&#xff0c;这里主要介绍下H…

#Uniapp:uni-app中vue2生命周期--11个

uni-app中vue2生命周期 生命周期钩子描述H5App端小程序说明beforeCreate在实例初始化之后被调用 详情√√√created在实例创建完成后被立即调用 详情√√√beforeMount在挂载开始之前被调用 详情√√√mounted挂载到实例上去之后调用 详情 注意&#xff1a;此处并不能确定子组…

GoLang:sql.Exec()报错

【报错内容】 Sorry, can not exec into mysql: Error 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ... 【原因】 sql.Exec 在大多数情况下只能执行单条SQL语句…

java面试题2024

前言 准备换工作了&#xff0c;给自己定个目标&#xff0c;每天至少整理出一道面试题。题型会比较随机&#xff0c;感觉这样更容易随机到面试官要问的东西。整理时我会把我认为正确的回答写出来&#xff0c;比较复杂的也尽量把原理贴出来&#xff0c;争取做到无论为了应付面试&…

数字化转型助力保险业腾飞,国产化安全产品护航高质量发展

近几年&#xff0c;全球贸易和经济受到了巨大冲击&#xff0c;众多贸易企业经营环境面临困难&#xff0c;某保险公司为国内企业提供强有力的保险保障&#xff0c;大大减轻了企业在国际贸易中风险&#xff0c;为国家经济恢复起到关键的作用。2022年&#xff0c;该保险公司承保金…

麒麟Linux安装新版微信的方法

麒麟Linux系统目前有v10和v10sp1&#xff0c;注意&#xff0c;恶趣味的是v10和v10sp1竟然不通用&#xff0c;这导致了一些国产程序出现运行bug,通过系统自带的麒麟商店无法图形界面安装&#xff0c;甚至搜索不到微信等等一系列问题&#xff0c;易用度确实很差。 解决办法也很简…

【一周年创作总结】人生是远方的无尽旷野呀

那一眼瞥见的伟大的灵魂&#xff0c;却似模糊的你和我 文章目录 &#x1f4d2;各个阶段的experience&#x1f50e;大一寒假&#x1f50e;大一下学期&#x1f50e;大一暑假&#x1f50e;大二上学期&#xff08;现在&#xff09; &#x1f354;相遇CSDN&#x1f6f8;自媒体&#…

Go gin框架控制器接收文件

1. 限定文件参数名&#xff08;一般做法&#xff09; func FileController(c *gin.Context) {//取表单中namefile的文件fileHeader, err : c.FormFile("file")//_, fileHeader, err : c.Request.FormFile("file")//这种也行if err ! nil {log.Println(err…