C++ MiniZip实现目录压缩与解压

Zlib是一个开源的数据压缩库,提供了一种通用的数据压缩和解压缩算法。它最初由Jean-Loup GaillyMark Adler开发,旨在成为一个高效、轻量级的压缩库,其被广泛应用于许多领域,包括网络通信、文件压缩、数据库系统等。其压缩算法是基于DEFLATE算法,这是一种无损数据压缩算法,通常能够提供相当高的压缩比。

在Zlib项目中的contrib目录下有一个minizip子项目,minizip实际上不是zlib库的一部分,而是一个独立的开源库,用于处理ZIP压缩文件格式。它提供了对ZIP文件的创建和解压的简单接口。minizip在很多情况下与zlib一起使用,因为ZIP压缩通常使用了DEFLATE压缩算法。通过对minizip库的二次封装则可实现针对目录的压缩与解压功能。

如果你想使用minizip通常你需要下载并编译它,然后将其链接到你的项目中。

  • Zlib源码:https://download.csdn.net/download/lyshark_csdn/88561117

编译Zlib库很简单,解压文件并进入到\zlib-1.3\contrib\vstudio目录下,根据自己编译器版本选择不同的目录,这里我选择vc12,进入后打开zlibvc.sln等待生成即可。

成功后可获得两个文件分别是zlibstat.libzlibwapi.lib如下图;

接着配置引用目录,这里需要多配置一个minizip头文件,该头文件是zlib里面的一个子项目。

lib库则需要包含zlibstat.libzlibwapi.lib这两个文件,此处读者可以自行放入到一个目录下;

ZIP 递归压缩目录

如下所示代码是一个使用zlib库实现的简单文件夹压缩工具的C++程序。该程序提供了压缩文件夹到 ZIP 文件的功能,支持递归地添加文件和子文件夹,利用了 Windows API 和 zlib 库的函数。

#define ZLIB_WINAPI
#include <string>
#include <iostream>
#include <vector>
#include <Shlwapi.h> 
#include <zip.h>
#include <unzip.h>
#include <zlib.h>

using namespace std;

#pragma comment(lib, "Shlwapi.lib")
#pragma comment(lib, "zlibstat.lib")

bool nyAddfiletoZip(zipFile zfile, const std::string& fileNameinZip, const std::string& srcfile)
{
  // 目录如果为空则直接返回
  if (NULL == zfile || fileNameinZip.empty())
  {
    return 0;
  }

  int nErr = 0;
  zip_fileinfo zinfo = { 0 };
  tm_zip tmz = { 0 };
  zinfo.tmz_date = tmz;
  zinfo.dosDate = 0;
  zinfo.internal_fa = 0;
  zinfo.external_fa = 0;

  char sznewfileName[MAX_PATH] = { 0 };
  memset(sznewfileName, 0x00, sizeof(sznewfileName));
  strcat_s(sznewfileName, fileNameinZip.c_str());
  if (srcfile.empty())
  {
    strcat_s(sznewfileName, "\\");
  }

  nErr = zipOpenNewFileInZip(zfile, sznewfileName, &zinfo, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_DEFAULT_COMPRESSION);
  if (nErr != ZIP_OK)
  {
    return false;
  }
  if (!srcfile.empty())
  {
    // 打开源文件
    FILE* srcfp = _fsopen(srcfile.c_str(), "rb", _SH_DENYNO);
    if (NULL == srcfp)
    {
      std::cout << "打开源文件失败" << std::endl;
      return false;
    }

    // 读入源文件写入zip文件
    int numBytes = 0;
    char* pBuf = new char[1024 * 100];
    if (NULL == pBuf)
    {
      std::cout << "新建缓冲区失败" << std::endl;
      return 0;
    }
    while (!feof(srcfp))
    {
      memset(pBuf, 0x00, sizeof(pBuf));
      numBytes = fread(pBuf, 1, sizeof(pBuf), srcfp);
      nErr = zipWriteInFileInZip(zfile, pBuf, numBytes);
      if (ferror(srcfp))
      {
        break;
      }
    }
    delete[] pBuf;
    fclose(srcfp);
  }
  zipCloseFileInZip(zfile);

  return true;
}

bool nyCollectfileInDirtoZip(zipFile zfile, const std::string& filepath, const std::string& parentdirName)
{
  if (NULL == zfile || filepath.empty())
  {
    return false;
  }
  bool bFile = false;
  std::string relativepath = "";
  WIN32_FIND_DATAA findFileData;

  char szpath[MAX_PATH] = { 0 };
  if (::PathIsDirectoryA(filepath.c_str()))
  {
    strcpy_s(szpath, sizeof(szpath) / sizeof(szpath[0]), filepath.c_str());
    int len = strlen(szpath) + strlen("\\*.*") + 1;
    strcat_s(szpath, len, "\\*.*");
  }
  else
  {
    bFile = true;
    strcpy_s(szpath, sizeof(szpath) / sizeof(szpath[0]), filepath.c_str());
  }

  HANDLE hFile = ::FindFirstFileA(szpath, &findFileData);
  if (NULL == hFile)
  {
    return false;
  }
  do
  {
    if (parentdirName.empty())
      relativepath = findFileData.cFileName;
    else
      // 生成zip文件中的相对路径
      relativepath = parentdirName + "\\" + findFileData.cFileName;

    // 如果是目录
    if (findFileData.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY)
    {
      // 去掉目录中的.当前目录和..前一个目录
      if (strcmp(findFileData.cFileName, ".") != 0 && strcmp(findFileData.cFileName, "..") != 0)
      {
        nyAddfiletoZip(zfile, relativepath, "");

        char szTemp[MAX_PATH] = { 0 };
        strcpy_s(szTemp, filepath.c_str());
        strcat_s(szTemp, "\\");
        strcat_s(szTemp, findFileData.cFileName);
        nyCollectfileInDirtoZip(zfile, szTemp, relativepath);
      }
      continue;
    }
    char szTemp[MAX_PATH] = { 0 };
    if (bFile)
    {
      //注意:处理单独文件的压缩
      strcpy_s(szTemp, filepath.c_str());
    }
    else
    {
      //注意:处理目录文件的压缩
      strcpy_s(szTemp, filepath.c_str());
      strcat_s(szTemp, "\\");
      strcat_s(szTemp, findFileData.cFileName);
    }

    nyAddfiletoZip(zfile, relativepath, szTemp);

  } while (::FindNextFileA(hFile, &findFileData));
  FindClose(hFile);

  return true;
}

/*
* 函数功能 : 压缩文件夹到目录
* 备    注 : dirpathName 源文件/文件夹
*      zipFileName 目的压缩包
*      parentdirName 压缩包内名字(文件夹名)
*/
bool nyCreateZipfromDir(const std::string& dirpathName, const std::string& zipfileName, const std::string& parentdirName)
{
  bool bRet = false;

  /*
  APPEND_STATUS_CREATE    创建追加
  APPEND_STATUS_CREATEAFTER 创建后追加(覆盖方式)
  APPEND_STATUS_ADDINZIP    直接追加
  */
  zipFile zFile = NULL;
  if (!::PathFileExistsA(zipfileName.c_str()))
  {
    zFile = zipOpen(zipfileName.c_str(), APPEND_STATUS_CREATE);
  }
  else
  {
    zFile = zipOpen(zipfileName.c_str(), APPEND_STATUS_ADDINZIP);
  }
  if (NULL == zFile)
  {
    std::cout << "创建ZIP文件失败" << std::endl;
    return bRet;
  }

  if (nyCollectfileInDirtoZip(zFile, dirpathName, parentdirName))
  {
    bRet = true;
  }

  zipClose(zFile, NULL);

  return bRet;
}
主要功能

nyCreateZipfromDir函数

bool nyCreateZipfromDir(const std::string& dirpathName, const std::string& zipfileName, const std::string& parentdirName);

功能:压缩文件夹到指定的 ZIP 文件。

参数:

  • dirpathName:源文件夹路径。
  • zipfileName:目标 ZIP 文件路径。
  • parentdirName:在 ZIP 文件内的文件夹名(如果为空则不指定目录)。

nyCollectfileInDirtoZip 函数

bool nyCollectfileInDirtoZip(zipFile zfile, const std::string& filepath, const std::string& parentdirName);

功能:递归地收集文件夹中的文件,并将它们添加到已打开的 ZIP 文件中。

参数:

  • zfile:已打开的 ZIP 文件。
  • filepath:文件夹路径。
  • parentdirName:在 ZIP 文件内的相对文件夹名。

nyAddfiletoZip 函数

bool nyAddfiletoZip(zipFile zfile, const std::string& fileNameinZip, const std::string& srcfile);

功能:将指定文件添加到已打开的 ZIP 文件中。

参数:

  • zfile:已打开的 ZIP 文件。
  • fileNameinZip:在 ZIP 文件内的相对文件路径。
  • srcfile:源文件路径。

程序流程

  • 文件夹压缩参数设置: 用户提供源文件夹路径、目标 ZIP 文件路径,以及在 ZIP 文件内的文件夹名。
  • ZIP 文件打开: 根据目标 ZIP 文件是否存在,使用 zipOpen 函数打开 ZIP 文件。
  • 文件夹递归添加: 使用 nyCollectfileInDirtoZip 函数递归地收集文件夹中的文件,并通过 nyAddfiletoZip 函数将它们添加到 ZIP 文件中。
  • ZIP 文件关闭: 使用 zipClose 函数关闭 ZIP 文件。
示例用法
int main(int argc, char* argv[])
{
	std::string dirpath = "D:\\lyshark\\test";                   // 源文件/文件夹
	std::string zipfileName = "D:\\lyshark\\test.zip";           // 目的压缩包
	
	bool ref = nyCreateZipfromDir(dirpath, zipfileName, "lyshark");          // 包内文件名<如果为空则压缩时不指定目录>

	std::cout << "[LyShark] 压缩状态 " << ref << std::endl;
	system("pause");
	return 0;
}

上述调用代码,参数1指定为需要压缩的文件目录,参数2指定为需要压缩成目录名,参数3为压缩后该目录的名字。

ZIP 递归解压目录

在这个C++程序中,实现了递归解压缩ZIP文件的功能。程序提供了以下主要功能:

  • replace_all 函数: 用于替换字符串中的指定子串。
  • CreatedMultipleDirectory 函数: 用于创建多级目录,确保解压缩时的目录结构存在。
  • UnzipFile 函数: 用于递归解压缩 ZIP 文件。该函数打开 ZIP 文件,获取文件信息,然后逐个解析和处理 ZIP 文件中的文件或目录。
#define ZLIB_WINAPI
#include <string>
#include <iostream>
#include <vector>
#include <Shlwapi.h> 
#include <zip.h>
#include <unzip.h>
#include <zlib.h>

using namespace std;

#pragma comment(lib, "Shlwapi.lib")
#pragma comment(lib, "zlibstat.lib")

// 将字符串内的old_value替换成new_value
std::string& replace_all(std::string& str, const std::string& old_value, const std::string& new_value)
{
	while (true)
	{
		std::string::size_type pos(0);
		if ((pos = str.find(old_value)) != std::string::npos)
			str.replace(pos, old_value.length(), new_value);
		else
			break;
	}
	return str;
}

// 创建多级目录
BOOL CreatedMultipleDirectory(const std::string& direct)
{
	std::string Directoryname = direct;
	if (Directoryname[Directoryname.length() - 1] != '\\')
	{
		Directoryname.append(1, '\\');
	}
	std::vector< std::string> vpath;
	std::string strtemp;
	BOOL  bSuccess = FALSE;
	for (int i = 0; i < Directoryname.length(); i++)
	{
		if (Directoryname[i] != '\\')
		{
			strtemp.append(1, Directoryname[i]);
		}
		else
		{
			vpath.push_back(strtemp);
			strtemp.append(1, '\\');
		}
	}
	std::vector< std::string>::iterator vIter = vpath.begin();
	for (; vIter != vpath.end(); vIter++)
	{
		bSuccess = CreateDirectoryA(vIter->c_str(), NULL) ? TRUE : FALSE;
	}
	return bSuccess;
}

/*
* 函数功能 : 递归解压文件目录
* 备    注 : strFilePath 压缩包路径
*      strTempPath 解压到
*/
void UnzipFile(const std::string& strFilePath, const std::string& strTempPath)
{
	int nReturnValue;
	string tempFilePath;
	string srcFilePath(strFilePath);
	string destFilePath;

	// 打开zip文件
	unzFile unzfile = unzOpen(srcFilePath.c_str());
	if (unzfile == NULL)
	{
		return;
	}

	// 获取zip文件的信息
	unz_global_info* pGlobalInfo = new unz_global_info;
	nReturnValue = unzGetGlobalInfo(unzfile, pGlobalInfo);
	if (nReturnValue != UNZ_OK)
	{
		std::cout << "数据包: " << pGlobalInfo->number_entry << endl;
		return;
	}

	// 解析zip文件
	unz_file_info* pFileInfo = new unz_file_info;
	char szZipFName[MAX_PATH] = { 0 };
	char szExtraName[MAX_PATH] = { 0 };
	char szCommName[MAX_PATH] = { 0 };

	// 存放从zip中解析出来的内部文件名
	for (int i = 0; i < pGlobalInfo->number_entry; i++)
	{
		// 解析得到zip中的文件信息
		nReturnValue = unzGetCurrentFileInfo(unzfile, pFileInfo, szZipFName, MAX_PATH, szExtraName, MAX_PATH, szCommName, MAX_PATH);
		if (nReturnValue != UNZ_OK)
			return;

		std::cout << "解压文件名: " << szZipFName << endl;

		string strZipFName = szZipFName;

		// 如果是目录则执行创建递归目录名
		if (pFileInfo->external_fa == FILE_ATTRIBUTE_DIRECTORY || (strZipFName.rfind('/') == strZipFName.length() - 1))
		{
			destFilePath = strTempPath + "//" + szZipFName;
			CreateDirectoryA(destFilePath.c_str(), NULL);
		}

		// 如果是文件则解压缩并创建
		else
		{
			// 创建文件 保存完整路径
			string strFullFilePath;
			tempFilePath = strTempPath + "/" + szZipFName;
			strFullFilePath = tempFilePath;

			int nPos = tempFilePath.rfind("/");
			int nPosRev = tempFilePath.rfind("\\");
			if (nPosRev == string::npos && nPos == string::npos)
				continue;

			size_t nSplitPos = nPos > nPosRev ? nPos : nPosRev;
			destFilePath = tempFilePath.substr(0, nSplitPos + 1);

			if (!PathIsDirectoryA(destFilePath.c_str()))
			{
				// 将路径格式统一
				destFilePath = replace_all(destFilePath, "/", "\\");
				// 创建多级目录
				int bRet = CreatedMultipleDirectory(destFilePath);
			}
			strFullFilePath = replace_all(strFullFilePath, "/", "\\");

			HANDLE hFile = CreateFileA(strFullFilePath.c_str(), GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_FLAG_WRITE_THROUGH, NULL);
			if (hFile == INVALID_HANDLE_VALUE)
			{
				return;
			}

			// 打开文件
			nReturnValue = unzOpenCurrentFile(unzfile);
			if (nReturnValue != UNZ_OK)
			{
				CloseHandle(hFile);
				return;
			}

			// 读取文件
			uLong BUFFER_SIZE = pFileInfo->uncompressed_size;;
			void* szReadBuffer = NULL;
			szReadBuffer = (char*)malloc(BUFFER_SIZE);
			if (NULL == szReadBuffer)
			{
				break;
			}

			while (TRUE)
			{
				memset(szReadBuffer, 0, BUFFER_SIZE);
				int nReadFileSize = 0;

				nReadFileSize = unzReadCurrentFile(unzfile, szReadBuffer, BUFFER_SIZE);

				// 读取文件失败
				if (nReadFileSize < 0)
				{
					unzCloseCurrentFile(unzfile);
					CloseHandle(hFile);
					return;
				}
				// 读取文件完毕
				else if (nReadFileSize == 0)
				{
					unzCloseCurrentFile(unzfile);
					CloseHandle(hFile);
					break;
				}
				// 写入读取的内容
				else
				{
					DWORD dWrite = 0;
					BOOL bWriteSuccessed = WriteFile(hFile, szReadBuffer, BUFFER_SIZE, &dWrite, NULL);
					if (!bWriteSuccessed)
					{
						unzCloseCurrentFile(unzfile);
						CloseHandle(hFile);
						return;
					}
				}
			}
			free(szReadBuffer);
		}
		unzGoToNextFile(unzfile);
	}

	delete pFileInfo;
	delete pGlobalInfo;

	// 关闭
	if (unzfile)
	{
		unzClose(unzfile);
	}
}
主要功能

replace_all 函数

std::string& replace_all(std::string& str, const std::string& old_value, const std::string& new_value)

功能:在字符串 str 中替换所有的 old_value 为 new_value。

参数:

  • str:待处理的字符串。
  • old_value:要被替换的子串。
  • new_value:替换后的新子串。

返回值:替换后的字符串。

CreatedMultipleDirectory 函数

BOOL CreatedMultipleDirectory(const std::string& direct)

功能:创建多级目录,确保路径存在。

参数:

  • direct:目录路径。
  • 返回值:如果成功创建目录返回 TRUE,否则返回 FALSE。

UnzipFile 函数

void UnzipFile(const std::string& strFilePath, const std::string& strTempPath)

功能:递归解压缩 ZIP 文件。

参数:

  • strFilePath:ZIP 文件路径。
  • strTempPath:解压到的目标路径。

该函数打开 ZIP 文件,获取文件信息,然后逐个解析和处理 ZIP 文件中的文件或目录。在解析过程中,根据文件或目录的属性,创建相应的目录结构,然后将文件写入目标路径。

示例用法
int main(int argc, char* argv[])
{
	std::string srcFilePath = "D:\\lyshark\\test.zip";
	std::string tempdir = "D:\\lyshark\\test";

	// 如果传入目录不存在则创建
	if (!::PathFileExistsA(tempdir.c_str()))
	{
		CreatedMultipleDirectory(tempdir);
	}

	// 调用解压函数
	UnzipFile(srcFilePath, tempdir);

	system("pause");
	return 0;
}

案例中,首先在解压缩之前判断传入目录是否存在,如果不存在则需要调用API创建目录,如果存在则直接调用UnzipFIle解压缩函数,实现解包,输出效果图如下;

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

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

相关文章

Doris数据模型的选择建议(十三)

Doris 的数据模型主要分为 3 类&#xff1a;Aggregate、Uniq、Duplicate Aggregate: Doris 数据模型-Aggregate 模型 Uniq&#xff1a;Doris 数据模型-Uniq 模型 Duplicate&#xff1a;Doris 数据模型-Duplicate 模型 因为数据模型在建表时就已经确定&#xff0c;且无法修改…

焦炉加热系统简述

烟道吸力 焦炉负压烘炉分烟道的吸力会影响立火道温度&#xff0c;具体影响因素如下&#xff1a; 烟道吸力过大会导致热量被抽走&#xff0c;使立火道温度降低。烟道吸力不足会导致烟气在烘炉内停留时间过长&#xff0c;使热量无法充分利用&#xff0c;也会导致立火道温度降低…

安防监控视频融合平台EasyCVR定制化页面开发

安防监控EasyCVR视频汇聚平台基于云边端智能协同&#xff0c;支持海量视频的轻量化接入与汇聚、转码与处理、全网智能分发、视频集中存储等。安防视频平台EasyCVR拓展性强&#xff0c;视频能力丰富&#xff0c;具体可实现视频监控直播、视频轮播、视频录像、云存储、回放与检索…

元素的点击操作

元素的点击操作 .click 语法 // 单击某个元素 .click()// 带参数的单击 .click(options)// 在某个位置点击 .click(position)// 在某个位置点击&#xff0c;且带参数 .click(position, options)// 根据页面坐标点击 .click(x, y)// 根据页面坐标点击&#xff0c;且带参数 .c…

冷链运输车辆GPS定位及温湿度管理案例

1.项目背景 项目名称&#xff1a;山西冷链运输车辆GPS定位及温湿度管理案例 项目需求&#xff1a;随着经济发展带动物流行业快速发展&#xff0c;运输规模逐步扩大&#xff0c;集团为了适应高速发展的行业现象&#xff0c;物流管理系统的完善成了现阶段发展的重中之重。因此&…

Elasticsearch:FMA 风格的向量相似度计算

作者&#xff1a;Chris Hegarty 在 Lucene 9.7.0 中&#xff0c;我们添加了利用 SIMD 指令执行向量相似性计算的数据并行化的支持。 现在&#xff0c;我们通过使用融合乘加 (Fused Mulitply-Add - FMA) 进一步推动这一点。 什么是 FMA 乘法和加法是一种常见的运算&#xff0c;…

echarts实现如下图功能代码

这里写自定义目录标题 const option {tooltip: {trigger: axis},legend: {left: "-1px",top:15px,type: "scroll",icon:rect,data: [{name:1, textStyle:{color: theme?"#E5EAF3":#303133,fontSize:14}}, {name: 2, textStyle:{color: theme…

超详细 | 实验室linux服务器非root账号 | 安装pip | 安装conda

登录实验室公用服务器&#xff0c;个人账号下&#xff08;非root&#xff09;是空的&#xff0c;啥也没有&#xff0c;想安装下pip和conda。 转了一圈&#xff0c;好像没太有针对这个需求写具体博客的&#xff0c;但有挺多讲直接在root下安的&#xff08;用的应该是个人虚拟机&…

【面试HOT300】滑动窗口篇

系列综述&#xff1a; &#x1f49e;目的&#xff1a;本系列是个人整理为了秋招面试的&#xff0c;整理期间苛求每个知识点&#xff0c;平衡理解简易度与深入程度。 &#x1f970;来源&#xff1a;材料主要源于【CodeTopHot300】进行的&#xff0c;每个知识点的修正和深入主要参…

【Vue】生命周期一文详解

目录 前言 生命周期 钩子函数使用方法 ​编辑 周期-----创建阶段 创建阶段做了些什么事 该阶段可以干什么 周期----挂载阶段 挂载阶段做了什么事 该阶段适合干什么 周期----更新阶段 更新阶段做了什么事 该阶段适合做什么 周期----销毁阶段 销毁阶段做了什么事 …

图解系列--密钥,随机数,应用技术

密钥 1.生成密钥 1.1.用随机数生成密钥 密码学用途的伪随机数生成器必须是专门针对密码学用途而设计的。 1.2.用口令生成密钥 一般都是将口令输入单向散列函数&#xff0c;然后将得到的散列值作为密钥使用。 在使用口令生成密钥时&#xff0c;为了防止字典攻击&#xff0c;需要…

【追求卓越13】算法--深度和广度优先算法

引导 前面的几个章节&#xff0c;我们介绍了树这种数据结构&#xff0c;二叉搜索树在进行查找方面比较高效&#xff1b;有二叉树演变来的堆数据结构在处理优先级队列&#xff0c;top K&#xff0c;中位数等问题比较优秀&#xff1b;今天我们继续介绍新的数据结构——图。它在解…

【20年扬大真题】编写程序,功能是给a数组输入30个数据,并以每行5个数据的形式输出

【20年扬大真题】编写程序&#xff0c;功能是给a数组输入30个数据&#xff0c;并以每行5个数据的形式输出 #include<stdio.h> int main() {int arr[30] { 0 };int i 0;printf("请输入30个数据&#xff1a;");for (i 0;i < 30;i){scanf("%d", …

C语言—指针入门

内存存放数据 如果发送指令&#xff0c;读取f变量的内容&#xff0c;则先找f - >10005这个字节&#xff0c;然后再找到123。 指针和指针变量 通常说的指针就是地址的意思&#xff0c;C中有专门的指针变量存放指针。一个指针占4个字节。 定义指针变量 类型名 *指针变量名…

童装店铺如何通过软文增加客流量

在信息超负载、媒介粉尘化、产品同质化多重因素下&#xff0c;传统营销疲态尽显、日渐式微&#xff0c;很难支撑新环境下品牌和企业的持续增长。聚焦童装行业更是如此&#xff0c;一方面用户迭代速度快&#xff0c;另一方面&#xff0c;新时代父母的育儿观念更加精细化&#xf…

跨境电商客服系统:提升客户满意度与优化电商体验的关键

随着全球电子商务的快速发展&#xff0c;跨境电商已经成为新的商业发展趋势。在这个高度竞争的市场环境中&#xff0c;优质的客户服务成为区分优秀与平庸的关键因素。一个高效的跨境电商客服系统不仅可以提高客户满意度&#xff0c;还能帮助企业优化电商体验&#xff0c;进而提…

电商领域的三大沉疴难题?实在智能RPA来帮你药到病除!

在电商市场规模迅速扩大的当下&#xff0c;厂家们为了保持自身的竞争力&#xff0c;纷纷走上了数智化转型的道路&#xff0c;明白学会使用自动化利器才是制胜之道。 如今从产业的生产线到运营商、物流运输、商品售前、商品售后&#xff0c;实在RPA在电商平台的应用已经十分广泛…

【EI会议征稿】第九届能源科学与化学工程国际学术研讨会 (ISESCE 2024)

第九届能源科学与化学工程国际学术研讨会 &#xff08;ISESCE 2024&#xff09; 2024 9th International Symposium on Energy Science and Chemical Engineering 第九届能源科学与化学工程国际学术研讨会&#xff08;ISESCE 2024&#xff09;定于2024年3月22-24日在中国南京…

创建 Springboot 项目

前言 创建 Spring Boot 项目是很多Java开发人员入门的重要一步&#xff01; 欢迎来到本篇关于创建 Spring Boot 项目的博客&#xff01;Spring Boot作为一个快速、便捷的开发框架&#xff0c;为我们提供了简化和加速应用程序开发的利器。 在这个数字化时代&#xff0c;快速响…