使用c++解压rar文件,基于UnRAR64,非命令行

最近项目需要解压缩rar文件,我们都知道rar是闭源收费软件,如果直接采用命令行可能会有限制,或者盗版问题,使用正版的winrar命令行解压rar文件是否有限制,这个我没来得及测试,但是从交互体验上来说,命令行对于很多情况的处理也不太友好,比如是否出错,比如异常处理,甚至某些高级功能,比如自定义解压缩的文件名等等,这些在命令行中不太好实现甚至无法实现。

网上很多关于c++解压rar的代码都是基于命令行的,这不是我想要的。

我想找能够解压rar的库或者dll,经过一番寻找,发现rarlab官网已经提供了UnRAR64,可以直接下载运行,这里提供一下下载地址:https://www.rarlab.com/rar/unrardll-624.exeicon-default.png?t=N7T8https://www.rarlab.com/rar/unrardll-624.exe

其他版本在这个页面:WinRAR archiver, a powerful tool to process RAR and ZIP filesicon-default.png?t=N7T8https://www.rarlab.com/rar_add.htm

使用官方的例子,即可实现winrar的解压缩,不过为了方便使用,我拓展了一下官方的代码,可以实现列出文件和解压缩rar文件到指定的目录,修改比较简单,这里直接上代码:

#pragma once


#include <windows.h>
#include <stdio.h>
#include <ctype.h>
#include <locale.h>
#include <vector>

#include "unrar.h"

enum { EXTRACT, TEST, PRINT, LIST };

int CommandLine(int Argc, char *Argv[]);

void ExtractArchive(char *ArcName,int Mode);
void ExtractArchive(char *ArcName, char* DstDir);
void ListArchive(char *ArcName);
size_t ListArchive(char *ArcName, std::vector<std::string>& vFiles);
void ShowComment(wchar_t *CmtBuf);
void OutHelp(void);

enum ERR_TYPE {ERR_OPEN, ERR_READ, ERR_PROCESS};
void OutError(int Error,char *ArcName,int ErrType);

void ShowArcInfo(unsigned int Flags,char *ArcName);
void PrintTime(const char *Label,unsigned int Low,unsigned int High);
void OutProcessFileError(int Error);
int CALLBACK CallbackProc(UINT msg,LPARAM UserData,LPARAM P1,LPARAM P2);

实现文件:

#define STRICT

#include "UnRDLL.h"

int CommandLine(int Argc, char *Argv[])
{
  setlocale(LC_ALL, NULL);

  if (Argc!=3)
  {
    OutHelp();
    return(0);
  }

  switch(toupper(Argv[1][0]))
  {
    case 'X':
      ExtractArchive(Argv[2],EXTRACT);
      break;
    case 'T':
      ExtractArchive(Argv[2],TEST);
      break;
    case 'P':
      ExtractArchive(Argv[2],PRINT);
      break;
    case 'L':
      ListArchive(Argv[2]);
      break;
    default:
      OutHelp();
      return(0);
  }

  return(0);
}


void ExtractArchive(char *ArcName,int Mode)
{
  HANDLE hArcData;
  int RHCode,PFCode;
  wchar_t CmtBuf[16384];
  struct RARHeaderData HeaderData;
  struct RAROpenArchiveDataEx OpenArchiveData;

  memset(&HeaderData,0,sizeof(HeaderData));
  memset(&OpenArchiveData,0,sizeof(OpenArchiveData));

  OpenArchiveData.ArcName=ArcName;
  OpenArchiveData.CmtBufW=CmtBuf;
  OpenArchiveData.CmtBufSize=sizeof(CmtBuf)/sizeof(CmtBuf[0]);
  OpenArchiveData.OpenMode=RAR_OM_EXTRACT;
  OpenArchiveData.Callback=CallbackProc;
  OpenArchiveData.UserData=Mode;
  hArcData=RAROpenArchiveEx(&OpenArchiveData);

  if (OpenArchiveData.OpenResult!=0)
  {
    OutError(OpenArchiveData.OpenResult,ArcName,ERR_OPEN);
    return;
  }

  ShowArcInfo(OpenArchiveData.Flags,ArcName);

  if (OpenArchiveData.CmtState==1)
    ShowComment(CmtBuf);

  while ((RHCode=RARReadHeader(hArcData,&HeaderData))==0)
  {
    switch(Mode)
    {
      case EXTRACT:
        printf("\nExtracting %-45s",HeaderData.FileName);
        break;
      case TEST:
        printf("\nTesting %-45s",HeaderData.FileName);
        break;
      case PRINT:
        printf("\nPrinting %-45s\n",HeaderData.FileName);
        break;
    }
    PFCode=RARProcessFile(hArcData,(Mode==EXTRACT) ? RAR_EXTRACT:RAR_TEST,
                          NULL,NULL);
    if (PFCode==0)
      printf(" OK");
    else
    {
      OutError(PFCode,ArcName,ERR_PROCESS);
      break;
    }
  }

  OutError(RHCode,ArcName,ERR_READ);

  RARCloseArchive(hArcData);
}


void ExtractArchive(char *ArcName, char* DstDir)
{
	HANDLE hArcData;
	int RHCode,PFCode;
	wchar_t CmtBuf[16384];
	struct RARHeaderData HeaderData;
	struct RAROpenArchiveDataEx OpenArchiveData;

	memset(&HeaderData,0,sizeof(HeaderData));
	memset(&OpenArchiveData,0,sizeof(OpenArchiveData));

	OpenArchiveData.ArcName=ArcName;
	OpenArchiveData.CmtBufW=CmtBuf;
	OpenArchiveData.CmtBufSize=sizeof(CmtBuf)/sizeof(CmtBuf[0]);
	OpenArchiveData.OpenMode=RAR_OM_EXTRACT;
	OpenArchiveData.Callback=CallbackProc;
	OpenArchiveData.UserData= EXTRACT;
	hArcData=RAROpenArchiveEx(&OpenArchiveData);

	if (OpenArchiveData.OpenResult!=0)
	{
		OutError(OpenArchiveData.OpenResult,ArcName,ERR_OPEN);
		return;
	}

	ShowArcInfo(OpenArchiveData.Flags,ArcName);

	if (OpenArchiveData.CmtState==1)
		ShowComment(CmtBuf);

	while ((RHCode=RARReadHeader(hArcData,&HeaderData))==0)
	{
		printf("\nExtracting %-45s",HeaderData.FileName);
		PFCode=RARProcessFile(hArcData,RAR_EXTRACT,DstDir,NULL);
		if (PFCode==0)
			printf(" OK");
		else
		{
			OutError(PFCode,ArcName,ERR_PROCESS);
			break;
		}
	}

	OutError(RHCode,ArcName,ERR_READ);

	RARCloseArchive(hArcData);
}

size_t ListArchive(char *ArcName, std::vector<std::string>& vFiles)
{
  HANDLE hArcData;
  int RHCode,PFCode;
  wchar_t CmtBuf[16384];
  struct RARHeaderDataEx HeaderData;
  struct RAROpenArchiveDataEx OpenArchiveData;
  wchar_t RedirName[1024];

  memset(&HeaderData,0,sizeof(HeaderData));
  memset(&OpenArchiveData,0,sizeof(OpenArchiveData));
  OpenArchiveData.ArcName=ArcName;
  OpenArchiveData.CmtBufW=CmtBuf;
  OpenArchiveData.CmtBufSize=sizeof(CmtBuf)/sizeof(CmtBuf[0]);
  OpenArchiveData.OpenMode=RAR_OM_LIST;
  OpenArchiveData.Callback=CallbackProc;
  OpenArchiveData.UserData=LIST;
  hArcData=RAROpenArchiveEx(&OpenArchiveData);

  if (OpenArchiveData.OpenResult!=0)
  {
    OutError(OpenArchiveData.OpenResult,ArcName,ERR_OPEN);
    return 0;
  }

  ShowArcInfo(OpenArchiveData.Flags,ArcName);

  if (OpenArchiveData.CmtState==1)
    ShowComment(CmtBuf);

  HeaderData.RedirName=RedirName;
  HeaderData.RedirNameSize=sizeof(RedirName)/sizeof(RedirName[0]);
  while ((RHCode=RARReadHeaderEx(hArcData,&HeaderData))==0)
  {
    __int64 UnpSize=HeaderData.UnpSize+(((__int64)HeaderData.UnpSizeHigh)<<32);
    __int64 PackSize=HeaderData.PackSize+(((__int64)HeaderData.PackSizeHigh)<<32);
    printf("\nName:   %s",HeaderData.FileName);

	vFiles.push_back(HeaderData.FileName);

    printf("\nSize:   %lld ",UnpSize);
    printf("\nPacked: %lld ",PackSize);

    PrintTime("mtime",HeaderData.MtimeLow,HeaderData.MtimeHigh);
    PrintTime("ctime",HeaderData.CtimeLow,HeaderData.CtimeHigh);
    PrintTime("atime",HeaderData.AtimeLow,HeaderData.AtimeHigh);

    if (HeaderData.RedirType!=0)
      printf("\n\tlink type %d, target %ls",HeaderData.RedirType,HeaderData.RedirName);
    if ((PFCode=RARProcessFile(hArcData,RAR_SKIP,NULL,NULL))!=0)
    {
      OutError(PFCode,ArcName,ERR_PROCESS);
      break;
    }
    printf("\n");
  }

  OutError(RHCode,ArcName,ERR_READ);

  RARCloseArchive(hArcData);

  return vFiles.size();
}

void ListArchive(char *ArcName)
{
  HANDLE hArcData;
  int RHCode,PFCode;
  wchar_t CmtBuf[16384];
  struct RARHeaderDataEx HeaderData;
  struct RAROpenArchiveDataEx OpenArchiveData;
  wchar_t RedirName[1024];

  memset(&HeaderData,0,sizeof(HeaderData));
  memset(&OpenArchiveData,0,sizeof(OpenArchiveData));
  OpenArchiveData.ArcName=ArcName;
  OpenArchiveData.CmtBufW=CmtBuf;
  OpenArchiveData.CmtBufSize=sizeof(CmtBuf)/sizeof(CmtBuf[0]);
  OpenArchiveData.OpenMode=RAR_OM_LIST;
  OpenArchiveData.Callback=CallbackProc;
  OpenArchiveData.UserData=LIST;
  hArcData=RAROpenArchiveEx(&OpenArchiveData);

  if (OpenArchiveData.OpenResult!=0)
  {
    OutError(OpenArchiveData.OpenResult,ArcName,ERR_OPEN);
    return;
  }

  ShowArcInfo(OpenArchiveData.Flags,ArcName);

  if (OpenArchiveData.CmtState==1)
    ShowComment(CmtBuf);

  HeaderData.RedirName=RedirName;
  HeaderData.RedirNameSize=sizeof(RedirName)/sizeof(RedirName[0]);
  while ((RHCode=RARReadHeaderEx(hArcData,&HeaderData))==0)
  {
    __int64 UnpSize=HeaderData.UnpSize+(((__int64)HeaderData.UnpSizeHigh)<<32);
    __int64 PackSize=HeaderData.PackSize+(((__int64)HeaderData.PackSizeHigh)<<32);
    printf("\nName:   %s",HeaderData.FileName);
    printf("\nSize:   %lld ",UnpSize);
    printf("\nPacked: %lld ",PackSize);

    PrintTime("mtime",HeaderData.MtimeLow,HeaderData.MtimeHigh);
    PrintTime("ctime",HeaderData.CtimeLow,HeaderData.CtimeHigh);
    PrintTime("atime",HeaderData.AtimeLow,HeaderData.AtimeHigh);

    if (HeaderData.RedirType!=0)
      printf("\n\tlink type %d, target %ls",HeaderData.RedirType,HeaderData.RedirName);
    if ((PFCode=RARProcessFile(hArcData,RAR_SKIP,NULL,NULL))!=0)
    {
      OutError(PFCode,ArcName,ERR_PROCESS);
      break;
    }
    printf("\n");
  }

  OutError(RHCode,ArcName,ERR_READ);

  RARCloseArchive(hArcData);
}


void ShowComment(wchar_t *CmtBuf)
{
  printf("\nComment:\n%ls\n",CmtBuf);
}


void OutHelp(void)
{
  printf("\nUNRDLL.   This is a simple example of UNRAR.DLL usage\n");
  printf("\nSyntax:\n");
  printf("\nUNRDLL X <Archive>     extract archive contents");
  printf("\nUNRDLL T <Archive>     test archive contents");
  printf("\nUNRDLL P <Archive>     print archive contents to stdout");
  printf("\nUNRDLL L <Archive>     view archive contents\n");
}


void OutError(int Error,char *ArcName,int ErrType)
{
  switch(Error)
  {
    case ERAR_NO_MEMORY:
      printf("\nNot enough memory");
      break;
    case ERAR_BAD_DATA:
      printf("\n%s: archive header or data are damaged",ArcName);
      break;
    case ERAR_BAD_ARCHIVE:
      printf("\n%s is not RAR archive",ArcName);
      break;
    case ERAR_UNKNOWN_FORMAT:
      printf("Unknown archive format");
      break;
    case ERAR_EOPEN:
      if (ErrType==ERR_PROCESS) // Returned by RARProcessFile.
        printf("Volume open error");
      else
        printf("\nCannot open %s",ArcName);
      break;
    case ERAR_ECREATE:
      printf("File create error");
      break;
    case ERAR_ECLOSE:
      printf("File close error");
      break;
    case ERAR_EREAD:
      printf("Read error");
      break;
    case ERAR_EWRITE:
      printf("Write error");
      break;
    case ERAR_SMALL_BUF:
      printf("Buffer for archive comment is too small, comment truncated");
      break;
    case ERAR_UNKNOWN:
      printf("Unknown error");
      break;
    case ERAR_MISSING_PASSWORD:
      printf("Password for encrypted file or header is not specified");
      break;
    case ERAR_EREFERENCE:
      printf("Cannot open file source for reference record");
      break;
    case ERAR_BAD_PASSWORD:
      printf("Wrong password is specified");
      break;
  }
}


void ShowArcInfo(unsigned int Flags,char *ArcName)
{
  printf("\nArchive %s\n",ArcName);
  printf("\nVolume:\t\t%s",(Flags & 1) ? "yes":"no");
  printf("\nComment:\t%s",(Flags & 2) ? "yes":"no");
  printf("\nLocked:\t\t%s",(Flags & 4) ? "yes":"no");
  printf("\nSolid:\t\t%s",(Flags & 8) ? "yes":"no");
  printf("\nNew naming:\t%s",(Flags & 16) ? "yes":"no");
  printf("\nRecovery:\t%s",(Flags & 64) ? "yes":"no");
  printf("\nEncr.headers:\t%s",(Flags & 128) ? "yes":"no");
  printf("\nFirst volume:\t%s",(Flags & 256) ? "yes":"no or older than 3.0");
  printf("\n---------------------------\n");
}


void PrintTime(const char *Label,unsigned int Low,unsigned int High)
{
  if (Low!=0 || High!=0)
  {
    FILETIME ft;
    ft.dwLowDateTime=Low;
    ft.dwHighDateTime=High;
    SYSTEMTIME ust,st;
    FileTimeToSystemTime(&ft,&ust);
    SystemTimeToTzSpecificLocalTime(NULL,&ust,&st);
    printf("\n%s:  %u-%02u-%02u %02u:%02u:%02u,%03u",Label,st.wYear,st.wMonth,
           st.wDay,st.wHour,st.wMinute,st.wSecond,st.wMilliseconds);
  }
}


int CALLBACK CallbackProc(UINT msg,LPARAM UserData,LPARAM P1,LPARAM P2)
{
  switch(msg)
  {
    case UCM_CHANGEVOLUMEW:
      if (P2==RAR_VOL_ASK)
      {
        printf("\n\nVolume %S is required\nPossible options:\n",(wchar_t *)P1);
        printf("\nEnter - try again");
        printf("\n'R'   - specify a new volume name");
        printf("\n'Q'   - quit");
        printf("\nEnter your choice: ");
        switch(toupper(getchar()))
        {
          case 'Q':
            return(-1);
          case 'R':
            {
              wchar_t *eol;
              printf("\nEnter new name: ");
              fflush(stdin);

              // fgetws may fail to read non-English characters from stdin
              // in some compilers. In this case use something more
              // appropriate for Unicode input.
              fgetws((wchar_t *)P1,MAX_PATH,stdin);

              eol=wcspbrk((wchar_t *)P1,L"\r\n");
              if (eol!=NULL)
                *eol=0;
            }
            return(1);
          default:
            return(1);
        }
      }
      if (P2==RAR_VOL_NOTIFY)
        printf("\n  ... volume %S\n",(wchar_t *)P1);
      return(1);
    case UCM_PROCESSDATA:
      if (UserData==PRINT)
      {
        fflush(stdout);
        fwrite((char *)P1,1,P2,stdout);
        fflush(stdout);
      }
      return(1);
    case UCM_NEEDPASSWORDW:
      {
        wchar_t *eol;
        printf("\nPassword required: ");
   
        // fgetws may fail to read non-English characters from stdin
        // in some compilers. In this case use something more appropriate
        // for Unicode input.
        fgetws((wchar_t *)P1,(int)P2,stdin);

        eol=wcspbrk((wchar_t *)P1,L"\r\n");
        if (eol!=NULL)
          *eol=0;
      }
      return(1);
  }
  return(0);
}

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

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

相关文章

EFCore: The ConnectionString property has not been initialized.

使用NuGet的程序包管理控制台执行命令“update-database”的时候报出该错误 经过检查发现是optionsBuilder.UseSqlServer(strConn);中的strConn没有写

【python海洋专题四十六】研究区域示意放大图

【python海洋专题四十六】研究区域示意放大图 图片 往期推荐 图片 【python海洋专题一】查看数据nc文件的属性并输出属性到txt文件 【python海洋专题二】读取水深nc文件并水深地形图 【python海洋专题三】图像修饰之画布和坐标轴 【Python海洋专题四】之水深地图图像修饰 …

穿越内存迷宫:C语言地址与指针的的冒险之旅

前言 C语言的魅力在于其直接的内存控制&#xff0c;而地址和指针是这种控制的核心。深入了解这些概念&#xff0c;将帮助我们更好地理解和利用C语言的潜力。本文将带领你踏上地址与指针的奇妙之旅&#xff0c;揭示它们在程序设计中的神秘面纱。 奇妙指针之旅&#xff1a;解码…

日语形容词分类

かっこいい的否定变形是かっこよくない

Selenium自动化测试细节讲解

与以前瀑布式开发模式不同&#xff0c;现在软件测试人员具有使用自动化工具执行测试用例套件的优势&#xff0c;而以前&#xff0c;测试人员习惯于通过测试脚本执行来完成测试。 但自动化测试的目的不是完全摆脱手动测试&#xff0c;而是最大程度地减少手动运行的测试。自动化…

计算机毕业设计选题推荐-农产品销售微信小程序/安卓APP-项目实战

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

抖音小程序开发:打造高效餐饮团购平台的技术指南

在餐饮行业&#xff0c;通过抖音小程序开发一个高效的团购平台&#xff0c;可以为餐厅提供更广泛的曝光&#xff0c;增加销售机会。本文将从技术角度出发&#xff0c;为您提供一份详细的抖音小程序开发指南&#xff0c;助您打造一流的餐饮团购平台。 一、确定需求和功能 在开…

让各大运营商都默默流泪的 HTTPS 协议(HTTPS 的加密流程)

文章目录 前言1. 什么是 HTTPS1.1 臭名昭著的 "运营商劫持" 2. 什么是"加密"3. HTTPS 的加密流程3.1 对称加密用对称加密可行吗&#xff1f; 3.2 引入非对称加密用对称加密非对称加密可行吗&#xff1f; 3.3 中间人攻击如何证明浏览器收到的公钥一定是该网…

栈 和 队列

什么是栈? 一种特殊的线性表&#xff0c;只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵守后进先出&#xff08;LIFO - Last In First Out&#xff09;的原则。   从数据结构的角度来看&…

【图文详解】Android Studio(新版本) 配置OpenCV库,解决出现的各种问题

前言 写这篇文章的目的就是记录自己在配置OpenCV库时遇到的问题。在网上查找相关资料时&#xff0c;发现很多Android Studio都是老版本&#xff0c;并且出现的问题都不能被解决。自己在配置过程中出现的问题都进行记录下来并一一解决。 新建项目 点击 New Project 选择界面 …

STM32F4X SDIO(九) 例程讲解-SD卡擦除、读写

STM32F4X SDIO &#xff08;九&#xff09; 例程讲解-SD卡擦除、读写 例程讲解-SD卡擦除、读写SD卡擦除CMD32:ERASE_WR_BLK_START命令发送命令响应 CMD33:ERASE_WR_BLK_END命令发送命令响应CMD38:ERASE命令响应 CMD13:SD_CMD_SEND_STATUS命令发送命令回应 SD卡读数据CMD16:SET_…

【LeetCode】挑战100天 Day10(热题+面试经典150题)

【LeetCode】挑战100天 Day10&#xff08;热题面试经典150题&#xff09; 一、LeetCode介绍二、LeetCode 热题 HOT 100-122.1 题目2.2 题解 三、面试经典 150 题-123.1 题目3.2 题解 一、LeetCode介绍 LeetCode是一个在线编程网站&#xff0c;提供各种算法和数据结构的题目&…

matplotlib 设置标签和图例

常用标签 xlabel&#xff1a;x轴标签名称。 ylabel&#xff1a;y轴标签名称。 title&#xff1a;图像标题。 设置x和y轴的刻度&#xff1a;xticks和yticks。 nums np.arange(0, 1.3, 0.01)# 设置标题 plt.title("title") # 设置横坐标信息 plt.xlabel("x-…

复杂度计算实例

1.常见时间复杂度计算举例 实例1 实例1基本操作执行了2N10次&#xff0c;通过推导大O阶方法知道&#xff0c;时间复杂度为 O(N) 实例2 实例2基本操作执行了MN次&#xff0c;有两个未知数M和N&#xff0c;时间复杂度为 O(NM) 实例3 实例3基本操作执行了100次&#xff0c;通过…

C++学习笔记(二):C++是如何运行的

C是如何运行的 include 预处理语句&#xff0c;在编译前就会被处理。 main函数 程序入口。 #include <iostream>int main() {std::cout << "Hello World!" << std::endl;std::cin.get();return 0; }Visual Studio 解决方案平台指的是编译的代码的…

探索微信小程序框架的精华——高质量的优秀选择

目录 引言&#xff1a; 1. 框架性能 2. 开发者工具支持 3. 文档和社区支持 4. 扩展能力 5. 使用率和稳定性 结语&#xff1a; 引言&#xff1a; 微信小程序作为一种轻量级、高效便捷的应用形式&#xff0c;已经在移动应用领域占据了重要地位。而其中&#xff0c;选择一个…

Nussbaumer Transform 以及 Amortized FHEW bootstrapping

参考文献&#xff1a; [Nuss80] Nussbaumer H. Fast polynomial transform methods for multidimensional DFTs[C]//ICASSP’80. IEEE International Conference on Acoustics, Speech, and Signal Processing. IEEE, 1980, 5: 235-237.[SV11] Smart N P, Vercauteren F. Full…

C++ 配合图形库实现画线效果

#include<stdio.h> #include <conio.h> #include<math.h> #include <graphics.h> // 引用图形库头文件 #define N 12 int List[N][N];void draw() {for (int i 0; i < N; i) {int x 200 * cos(2 * 3.14 * i / N);int y 200 * sin(2 * 3.1…

归并排序 merge Sort + 图解 + 递归 / 非递归

归并排序(merge sort)的主要思想是&#xff1a;将若干个有序序列逐步归并&#xff0c;最终归并为一个有序序列二路归并排序(2-way merge sort)是归并排序中最简单的排序方法 &#xff08;1&#xff09;二路归并排序的递归实现 // 二路归并排序的递归实现 void merge(vector&l…

Ocelot:.NET开源API网关提供路由管理、服务发现、鉴权限流等功能

随着微服务的兴起&#xff0c;API网关越来越常见。API网关是连接应用程序和用户之间的桥梁&#xff0c;就像一个交通指挥员&#xff0c;负责处理所有进出应用的数据和请求&#xff0c;确保安全、高效、有序地流通。 今天给大家推荐一个.NET开源API网关。 01 项目简介 Ocelot…