最原始的版本:代码结构说明
这段代码的主要功能是:
从指定文件夹中读取所有 XML 文件。
将每个 XML 文件的内容通过 HTTP POST 请求发送到指定的 API 地址。
处理服务器的响应,并记录每个文件的处理结果。
代码功能详解
-
文件夹路径和 API 配置:
-
xmlFolderPath:指定存放 XML 文件的文件夹路径。 -
apiUrl:华为服务器的 API 地址。 -
appId和appKey:用于身份验证的 AppId 和 AppKey。 -
topic和tag:消息的主题和标签。
-
-
读取文件夹中的 XML 文件:
-
使用
Directory.GetFiles方法获取文件夹中所有.xml文件。 -
如果文件夹中没有文件,输出提示并退出程序。
-
-
遍历并处理每个 XML 文件:
-
使用
foreach循环遍历每个文件。 -
调用
ReadXmlFile方法读取文件内容。 -
调用
SendHttpRequest方法将文件内容发送到服务器。 -
捕获并处理可能发生的异常(如文件格式错误、读取失败、HTTP 请求失败等)。
-
-
读取 XML 文件内容:
-
使用
StreamReader读取文件内容,并指定文件编码为GB2312。 -
如果文件不存在,抛出
FileNotFoundException异常。
-
-
发送 HTTP 请求:
-
使用
HttpWebRequest创建 HTTP POST 请求。 -
设置请求头(AppId、AppKey、Topic、Tag)。
-
将 XML 内容写入请求体。
-
获取服务器响应并返回响应内容。
-
-
异常处理:
-
对文件读取、XML 解析、HTTP 请求等操作进行异常捕获,确保程序健壮性。
-
代码运行流程
-
程序启动后,检查指定文件夹中是否存在 XML 文件。
-
如果存在文件,逐个读取文件内容并发送到服务器。
-
每次发送后,输出服务器的响应内容。
-
如果发生错误,输出错误信息并继续处理下一个文件。
注意事项
-
文件编码:
-
代码中假设 XML 文件的编码是
GB2312,如果文件编码不同,需要调整StreamReader的编码参数。
-
-
HTTP 请求超时:
-
如果服务器响应较慢,可能需要设置
HttpWebRequest.Timeout属性,避免请求长时间挂起。
-
-
异常处理:
-
代码中对常见异常进行了捕获,但可以根据实际需求进一步细化异常处理逻辑。
-
如果有其他问题,欢迎继续讨论!
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Xml;
namespace EMS数据回传
{
class Program
{
static void Main(string[] args)
{
// XML 文件夹路径
string xmlFolderPath = @"Z:\toMesXml";
// 服务器 API 地址
string apiUrl = "服务器 API 地址";
// 你的 AppId
string appId = "你的 AppId";
// 你的 AppKey
string appKey = "你的 AppKey";
// Topic 名称
string topic = "Topic 名称";
// 消息 Tag
string tag = "消息 Tag";
try
{
// 获取文件夹中的所有 XML 文件
string[] xmlFiles = Directory.GetFiles(xmlFolderPath, "*.xml");
// 如果文件夹中没有 XML 文件,输出提示并退出
if (xmlFiles.Length == 0)
{
Console.WriteLine("文件夹中没有 XML 文件。");
return;
}
// 遍历每个 XML 文件并发送
foreach (string xmlFile in xmlFiles)
{
try
{
// 输出当前正在处理的文件
Console.WriteLine(string.Format("正在处理文件: {0}", xmlFile));
// 1. 读取 XML 文件内容
string xmlContent = ReadXmlFile(xmlFile);
// 2. 发送 HTTP 请求
string response = SendHttpRequest(apiUrl, appId, appKey, topic, tag, xmlContent);
// 3. 处理响应,输出发送成功的信息
Console.WriteLine(string.Format("文件 {0} 发送成功,服务器响应: {1}", xmlFile, response));
}
catch (XmlException ex)
{
// 捕获 XML 格式错误
Console.WriteLine(string.Format("文件 {0} 格式错误: {1}", xmlFile, ex.Message));
}
catch (IOException ex)
{
// 捕获文件读取失败错误
Console.WriteLine(string.Format("文件 {0} 读取失败: {1}", xmlFile, ex.Message));
}
catch (WebException ex)
{
// 捕获 HTTP 请求失败错误
Console.WriteLine(string.Format("文件 {0} 发送失败,HTTP 错误: {1}", xmlFile, ex.Message));
}
catch (Exception ex)
{
// 捕获其他未知错误
Console.WriteLine(string.Format("文件 {0} 发生未知错误: {1}", xmlFile, ex.Message));
}
}
}
catch (Exception ex)
{
// 捕获全局错误
Console.WriteLine("发生错误: " + ex.Message);
}
}
// 读取 XML 文件内容
static string ReadXmlFile(string filePath)
{
// 检查文件是否存在,如果不存在则抛出异常
if (!File.Exists(filePath))
{
throw new FileNotFoundException("XML 文件未找到: " + filePath);
}
// 使用 StreamReader 指定编码读取文件
using (StreamReader reader = new StreamReader(filePath, Encoding.GetEncoding("GB2312"))) // 根据文件编码调整
{
// 读取文件的全部内容并返回
string xmlContent = reader.ReadToEnd();
return xmlContent;
}
}
// 发送 HTTP 请求
static string SendHttpRequest(string url, string appId, string appKey, string topic, string tag, string xmlContent)
{
// 创建 HTTP 请求
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
// 设置请求方法为 POST
request.Method = "POST";
// 设置请求内容类型为 XML
request.ContentType = "application/xml";
// 添加请求头
request.Headers.Add("X-HW-ID", appId); // AppId
request.Headers.Add("X-HW-APPKEY", appKey); // AppKey
request.Headers.Add("MsgTopic", topic); // Topic
request.Headers.Add("MsgTag", tag); // Tag
// 将 XML 内容转换为字节数组
byte[] data = Encoding.UTF8.GetBytes(xmlContent);
// 设置请求内容的长度
request.ContentLength = data.Length;
// 将 XML 内容写入请求体
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(data, 0, data.Length);
}
// 获取服务器响应
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
using (Stream responseStream = response.GetResponseStream())
using (StreamReader reader = new StreamReader(responseStream, Encoding.UTF8))
{
// 读取服务器响应的内容并返回
return reader.ReadToEnd();
}
}
}
}
第一版本代码改进,当然,我会在代码中对改动的地方进行标记。以下是改进后的代码,并在改动处添加了注释以突出显示:
### 改动标记说明:
1. **配置 `HttpClient`**:
- **新增方法**: `ConfigureHttpClient` 方法用于配置 `HttpClient`。
2. **日志输出**:
- **新增日志输出**: 在关键步骤增加了详细的日志输出,如开始处理时间、发现的文件数量、处理进度、成功上传的数量和结束时间等。
3. **错误处理**:
- **新增错误日志记录**: 在捕获异常时调用 `LogError` 方法记录详细的错误信息。
4. **重试机制**:
- **新增方法**: `SendHttpRequestWithRetryAsync` 方法实现了带有重试机制的 HTTP 请求发送。
5. **异步处理**:
- **异步方法调用**: 在多个地方使用 `await` 关键字进行异步操作,如 `SendHttpRequestWithRetryAsync` 和 `SendHttpRequestAsync`。
6. **资源管理**:
- **确保资源释放**: 在 `finally` 块中调用 `httpClient.Dispose()` 确保资源得到释放。
7. **性能优化**:
- **批量处理**: 使用 LINQ 进行批量处理,减少循环次数和方法调用开销。
这些改动显著提升了代码的性能、可靠性和可维护性。
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Linq;
namespace EMS数据回传
{
class Program
{
// 创建一个静态的 HttpClient 实例以提高性能
private static readonly HttpClient httpClient = new HttpClient();
static async Task Main(string[] args)
{
// 注册编码提供程序,支持 GB2312
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
// 配置参数
string baseFolderPath = @"Z:\彩盒CCD"; // 基础文件夹路径
string apiUrl = ""; // 华为服务器 API 地址
string appId = ""; // 你的 AppId
string appKey = ""; // 你的 AppKey
string topic = ""; // Topic 名称
string tag = ""; // 消息 Tag
string uploadedFilesLog = "uploaded_files.txt"; // 记录已上传文件的日志文件
int maxRetries = 3; // 最大重试次数
try
{
// 配置 HttpClient (新增方法调用)
ConfigureHttpClient(apiUrl, appId, appKey, topic, tag); // 改动
// 加载已上传的文件记录
HashSet<string> uploadedFiles = LoadUploadedFiles(uploadedFilesLog);
int totalProcessed = 0;
int totalSuccess = 0;
Console.WriteLine($"开始处理,时间: {DateTime.Now}"); // 新增日志输出
// 遍历 1-19 线
for (int lineNumber = 1; lineNumber <= 19; lineNumber++)
{
string lineFolder = Path.Combine(baseFolderPath, $"{lineNumber}线");
string toMesXmlFolder = Path.Combine(lineFolder, "toMesXml");
// 检查 toMesXml 文件夹是否存在
if (!Directory.Exists(toMesXmlFolder))
{
Console.WriteLine($"文件夹 {toMesXmlFolder} 不存在,跳过处理。");
continue;
}
// 获取 toMesXml 文件夹中的所有 XML 文件
string[] xmlFiles = Directory.GetFiles(toMesXmlFolder, "*.xml");
if (xmlFiles.Length == 0)
{
Console.WriteLine($"文件夹 {toMesXmlFolder} 中没有 XML 文件。");
continue;
}
Console.WriteLine($"发现 {xmlFiles.Length} 个XML文件在 {lineNumber}线"); // 新增日志输出
// 遍历每个 XML 文件并发送
foreach (string xmlFile in xmlFiles)
{
try
{
string fileName = Path.GetFileName(xmlFile);
totalProcessed++;
// 检查文件是否已经上传过
if (uploadedFiles.Contains(fileName))
{
Console.WriteLine($"文件 {fileName} 已经上传过,跳过处理。");
continue;
}
Console.WriteLine($"正在处理文件: {fileName}");
// 1. 读取 XML 文件内容
string xmlContent = ReadXmlFile(xmlFile);
// 2. 发送 HTTP 请求(带重试机制)
string response = await SendHttpRequestWithRetryAsync(xmlContent, maxRetries); // 异步方法调用
// 3. 处理响应
Console.WriteLine($"文件 {fileName} 发送成功,服务器响应: {response}");
totalSuccess++;
// 4. 记录已上传的文件名
uploadedFiles.Add(fileName);
File.AppendAllText(uploadedFilesLog, fileName + Environment.NewLine);
}
catch (Exception ex)
{
LogError($"文件 {xmlFile} 发生错误", ex); // 新增错误日志记录
}
}
}
Console.WriteLine($"处理完成,总共处理: {totalProcessed} 个文件,成功: {totalSuccess} 个,时间: {DateTime.Now}"); // 新增总结日志输出
}
catch (Exception ex)
{
LogError("程序执行过程中发生错误", ex); // 新增错误日志记录
}
finally
{
httpClient.Dispose(); // 确保资源释放
}
}
// 配置 HttpClient (新增方法)
static void ConfigureHttpClient(string apiUrl, string appId, string appKey, string topic, string tag)
{
httpClient.BaseAddress = new Uri(apiUrl);
httpClient.DefaultRequestHeaders.Clear();
httpClient.DefaultRequestHeaders.Add("X-HW-ID", appId);
httpClient.DefaultRequestHeaders.Add("X-HW-APPKEY", appKey);
httpClient.DefaultRequestHeaders.Add("MsgTopic", topic);
httpClient.DefaultRequestHeaders.Add("MsgTag", tag);
}
// 读取 XML 文件内容 (保持不变)
static string ReadXmlFile(string filePath)
{
if (!File.Exists(filePath))
{
throw new FileNotFoundException("XML 文件未找到: " + filePath);
}
// 使用 StreamReader 指定编码读取文件
using (StreamReader reader = new StreamReader(filePath, Encoding.GetEncoding("GB2312"))) // 根据文件编码调整
{
string xmlContent = reader.ReadToEnd();
return xmlContent;
}
}
// 发送 HTTP 请求(带重试机制) (新增方法)
static async Task<string> SendHttpRequestWithRetryAsync(string xmlContent, int maxRetries)
{
int retryCount = 0;
Exception lastException = null;
while (retryCount < maxRetries)
{
try
{
return await SendHttpRequestAsync(xmlContent); // 异步方法调用
}
catch (Exception ex)
{
lastException = ex;
retryCount++;
if (retryCount < maxRetries)
{
int delayMs = 1000 * retryCount; // 递增延迟
Console.WriteLine($"请求失败,{delayMs/1000}秒后进行第{retryCount}次重试...");
await Task.Delay(delayMs); // 异步延迟
}
}
}
throw new Exception($"在{maxRetries}次尝试后仍然失败", lastException);
}
// 发送 HTTP 请求(异步) (新增方法)
static async Task<string> SendHttpRequestAsync(string xmlContent)
{
// 准备请求内容
var content = new StringContent(xmlContent, Encoding.UTF8, "application/xml");
// 发送请求
var response = await httpClient.PostAsync("", content); // 异步请求
// 确保请求成功
response.EnsureSuccessStatusCode();
// 读取响应
return await response.Content.ReadAsStringAsync(); // 异步读取响应
}
// 加载已上传的文件记录 (优化)
static HashSet<string> LoadUploadedFiles(string logFilePath)
{
HashSet<string> uploadedFiles = new HashSet<string>();
// 如果日志文件存在,读取已上传的文件名
if (File.Exists(logFilePath))
{
try
{
string[] lines = File.ReadAllLines(logFilePath);
uploadedFiles = new HashSet<string>(lines.Select(line => line.Trim())); // 使用 LINQ 进行批量处理
Console.WriteLine($"已加载 {uploadedFiles.Count} 个已上传文件记录"); // 新增日志输出
}
catch (Exception ex)
{
LogError("读取已上传文件记录时出错", ex); // 新增错误日志记录
}
}
else
{
Console.WriteLine($"上传记录文件 {logFilePath} 不存在,将创建新文件"); // 新增日志输出
}
return uploadedFiles;
}
// 记录错误信息 (新增方法)
static void LogError(string message, Exception ex)
{
Console.WriteLine($"错误: {message}");
Console.WriteLine($"异常信息: {ex.Message}");
Console.WriteLine($"堆栈跟踪: {ex.StackTrace}");
// 记录到错误日志文件
string errorLog = $"error_log_{DateTime.Now:yyyyMMdd}.txt";
string errorMessage = $"[{DateTime.Now}] {message}\r\n{ex.Message}\r\n{ex.StackTrace}\r\n\r\n";
File.AppendAllText(errorLog, errorMessage);
}
}
}
转写成C++代码
关键点说明:
- HTTP 请求:使用
libcurl库发送 POST 请求。 - 文件操作:使用 C++17 的
<filesystem>库处理文件和目录。 - 编码支持:如果需要支持 GB2312 编码,可以使用
iconv或其他第三方库。 - 日志记录:通过文件流记录已上传的文件名。
如何运行:
- 安装
libcurl和filesystem支持的编译器(如 GCC 或 MSVC)。 - 将代码保存为
.cpp文件,并编译运行。 - 确保配置正确的路径和 API 参数。
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <unordered_set>
#include <filesystem>
#include <chrono>
#include <thread>
#include <curl/curl.h>
namespace fs = std::filesystem;
// 配置参数
const std::string baseFolderPath = "Z:/彩盒CCD"; // 基础文件夹路径
const std::string apiUrl = "https://apigw-cn-south.huawei.com/api/mqs/message"; // 华为服务器 API 地址
const std::string appId = "huawei.supply.mes.mdip.wh.genuine_opto"; // 你的 AppId
const std::string appKey = "bWe96ZRDRrxzY2YMHIS5jQ=="; // 你的 AppKey
const std::string topic = "T_EMS_AOI"; // Topic 名称
const std::string tag = "HGZYXG"; // 消息 Tag
const std::string uploadedFilesLog = "uploaded_files.txt"; // 记录已上传文件的日志文件
const int maxRetries = 3; // 最大重试次数
// libcurl 写入回调函数
size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* output) {
size_t totalSize = size * nmemb;
output->append((char*)contents, totalSize);
return totalSize;
}
// 配置 libcurl
CURL* ConfigureCurl(const std::string& xmlContent) {
CURL* curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, apiUrl.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, nullptr); // 设置请求头
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, xmlContent.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &xmlContent);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_slist_append(nullptr, ("X-HW-ID: " + appId).c_str()));
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_slist_append(nullptr, ("X-HW-APPKEY: " + appKey).c_str()));
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_slist_append(nullptr, ("MsgTopic: " + topic).c_str()));
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_slist_append(nullptr, ("MsgTag: " + tag).c_str()));
}
return curl;
}
// 发送 HTTP 请求(带重试机制)
std::string SendHttpRequestWithRetry(const std::string& xmlContent) {
int retryCount = 0;
while (retryCount < maxRetries) {
CURL* curl = ConfigureCurl(xmlContent);
CURLcode res = curl_easy_perform(curl);
if (res == CURLE_OK) {
long httpCode = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode);
if (httpCode == 200) {
std::string response;
curl_easy_cleanup(curl);
return response;
}
}
retryCount++;
std::this_thread::sleep_for(std::chrono::seconds(retryCount));
}
throw std::runtime_error("HTTP 请求失败");
}
// 读取 XML 文件内容
std::string ReadXmlFile(const std::string& filePath) {
std::ifstream file(filePath, std::ios::binary);
if (!file.is_open()) {
throw std::runtime_error("无法打开文件: " + filePath);
}
std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
return content;
}
// 加载已上传的文件记录
std::unordered_set<std::string> LoadUploadedFiles(const std::string& logFilePath) {
std::unordered_set<std::string> uploadedFiles;
if (fs::exists(logFilePath)) {
std::ifstream logFile(logFilePath);
std::string line;
while (std::getline(logFile, line)) {
uploadedFiles.insert(line);
}
}
return uploadedFiles;
}
// 主函数
int main() {
try {
// 加载已上传的文件记录
auto uploadedFiles = LoadUploadedFiles(uploadedFilesLog);
int totalProcessed = 0;
int totalSuccess = 0;
std::cout << "开始处理,时间: " << std::chrono::system_clock::now() << std::endl;
// 遍历 1-19 线
for (int lineNumber = 1; lineNumber <= 19; lineNumber++) {
std::string lineFolder = baseFolderPath + "/" + std::to_string(lineNumber) + "线";
std::string toMesXmlFolder = lineFolder + "/toMesXml";
// 检查 toMesXml 文件夹是否存在
if (!fs::exists(toMesXmlFolder)) {
std::cout << "文件夹 " << toMesXmlFolder << " 不存在,跳过处理。" << std::endl;
continue;
}
// 获取 toMesXml 文件夹中的所有 XML 文件
std::vector<std::string> xmlFiles;
for (const auto& entry : fs::directory_iterator(toMesXmlFolder)) {
if (entry.path().extension() == ".xml") {
xmlFiles.push_back(entry.path().string());
}
}
if (xmlFiles.empty()) {
std::cout << "文件夹 " << toMesXmlFolder << " 中没有 XML 文件。" << std::endl;
continue;
}
std::cout << "发现 " << xmlFiles.size() << " 个XML文件在 " << lineNumber << "线" << std::endl;
// 遍历每个 XML 文件并发送
for (const auto& xmlFile : xmlFiles) {
try {
std::string fileName = fs::path(xmlFile).filename().string();
totalProcessed++;
// 检查文件是否已经上传过
if (uploadedFiles.count(fileName)) {
std::cout << "文件 " << fileName << " 已经上传过,跳过处理。" << std::endl;
continue;
}
std::cout << "正在处理文件: " << fileName << std::endl;
// 读取 XML 文件内容
std::string xmlContent = ReadXmlFile(xmlFile);
// 发送 HTTP 请求
std::string response = SendHttpRequestWithRetry(xmlContent);
// 处理响应
std::cout << "文件 " << fileName << " 发送成功,服务器响应: " << response << std::endl;
totalSuccess++;
// 记录已上传的文件名
uploadedFiles.insert(fileName);
std::ofstream logFile(uploadedFilesLog, std::ios::app);
logFile << fileName << std::endl;
} catch (const std::exception& ex) {
std::cerr << "文件 " << xmlFile << " 发生错误: " << ex.what() << std::endl;
}
}
}
std::cout << "处理完成,总共处理: " << totalProcessed << " 个文件,成功: " << totalSuccess << " 个,时间: "
<< std::chrono::system_clock::now() << std::endl;
} catch (const std::exception& ex) {
std::cerr << "程序执行过程中发生错误: " << ex.what() << std::endl;
}
return 0;
}
代码改写成面向对象的风格,可以更好地组织代码结构、提高可读性和可维护性。以下是改写后的版本:
设计说明
-
AppConfig类:- 用于集中管理配置参数。
- 提高代码的可维护性和可扩展性。
-
HttpRequest类:- 封装了与 HTTP 请求相关的逻辑。
- 包括发送请求、配置 libcurl 和处理回调。
-
FileProcessor类:- 负责文件的读取和日志记录。
- 将文件操作独立出来,便于复用和测试。
-
XmlUploader类:- 主逻辑类,负责遍历文件夹、处理文件并调用其他类的功能。
- 使用
totalProcessed和totalSuccess记录统计信息。
-
主函数:
- 创建
XmlUploader对象并调用其run方法启动程序。
- 创建
优点
- 模块化:每个类负责一个特定的功能,职责清晰。
- 可扩展性:如果需要修改某个功能(如增加新的文件格式支持),只需修改对应类。
- 易测试性:每个类可以单独进行单元测试。
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <unordered_set>
#include <filesystem>
#include <chrono>
#include <thread>
#include <curl/curl.h>
namespace fs = std::filesystem;
// 配置参数类
class AppConfig {
public:
static const std::string baseFolderPath;
static const std::string apiUrl;
static const std::string appId;
static const std::string appKey;
static const std::string topic;
static const std::string tag;
static const std::string uploadedFilesLog;
static const int maxRetries;
};
const std::string AppConfig::baseFolderPath = "Z:/彩盒CCD";
const std::string AppConfig::apiUrl = "https://apigw-cn-south.huawei.com/api/mqs/message";
const std::string AppConfig::appId = "huawei.supply.mes.mdip.wh.genuine_opto";
const std::string AppConfig::appKey = "bWe96ZRDRrxzY2YMHIS5jQ==";
const std::string AppConfig::topic = "T_EMS_AOI";
const std::string AppConfig::tag = "HGZYXG";
const std::string AppConfig::uploadedFilesLog = "uploaded_files.txt";
const int AppConfig::maxRetries = 3;
// HTTP 请求类
class HttpRequest {
public:
static std::string sendWithRetry(const std::string& xmlContent) {
int retryCount = 0;
while (retryCount < AppConfig::maxRetries) {
CURL* curl = configureCurl(xmlContent);
CURLcode res = curl_easy_perform(curl);
if (res == CURLE_OK) {
long httpCode = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode);
if (httpCode == 200) {
std::string response;
curl_easy_cleanup(curl);
return response;
}
}
retryCount++;
std::this_thread::sleep_for(std::chrono::seconds(retryCount));
}
throw std::runtime_error("HTTP 请求失败");
}
private:
static CURL* configureCurl(const std::string& xmlContent) {
CURL* curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, AppConfig::apiUrl.c_str());
struct curl_slist* headers = nullptr;
headers = curl_slist_append(headers, ("X-HW-ID: " + AppConfig::appId).c_str());
headers = curl_slist_append(headers, ("X-HW-APPKEY: " + AppConfig::appKey).c_str());
headers = curl_slist_append(headers, ("MsgTopic: " + AppConfig::topic).c_str());
headers = curl_slist_append(headers, ("MsgTag: " + AppConfig::tag).c_str());
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, xmlContent.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &xmlContent);
}
return curl;
}
static size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* output) {
size_t totalSize = size * nmemb;
output->append((char*)contents, totalSize);
return totalSize;
}
};
// 文件处理类
class FileProcessor {
public:
static std::string readFile(const std::string& filePath) {
std::ifstream file(filePath, std::ios::binary);
if (!file.is_open()) {
throw std::runtime_error("无法打开文件: " + filePath);
}
return std::string((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
}
static std::unordered_set<std::string> loadUploadedFiles(const std::string& logFilePath) {
std::unordered_set<std::string> uploadedFiles;
if (fs::exists(logFilePath)) {
std::ifstream logFile(logFilePath);
std::string line;
while (std::getline(logFile, line)) {
uploadedFiles.insert(line);
}
}
return uploadedFiles;
}
static void logUploadedFile(const std::string& fileName, const std::string& logFilePath) {
std::ofstream logFile(logFilePath, std::ios::app);
logFile << fileName << std::endl;
}
};
// 主逻辑类
class XmlUploader {
public:
XmlUploader() : totalProcessed(0), totalSuccess(0) {}
void run() {
try {
auto uploadedFiles = FileProcessor::loadUploadedFiles(AppConfig::uploadedFilesLog);
std::cout << "开始处理,时间: " << std::chrono::system_clock::now() << std::endl;
for (int lineNumber = 1; lineNumber <= 19; lineNumber++) {
processLine(lineNumber, uploadedFiles);
}
std::cout << "处理完成,总共处理: " << totalProcessed << " 个文件,成功: " << totalSuccess << " 个,时间: "
<< std::chrono::system_clock::now() << std::endl;
} catch (const std::exception& ex) {
std::cerr << "程序执行过程中发生错误: " << ex.what() << std::endl;
}
}
private:
int totalProcessed;
int totalSuccess;
void processLine(int lineNumber, std::unordered_set<std::string>& uploadedFiles) {
std::string lineFolder = AppConfig::baseFolderPath + "/" + std::to_string(lineNumber) + "线";
std::string toMesXmlFolder = lineFolder + "/toMesXml";
if (!fs::exists(toMesXmlFolder)) {
std::cout << "文件夹 " << toMesXmlFolder << " 不存在,跳过处理。" << std::endl;
return;
}
std::vector<std::string> xmlFiles;
for (const auto& entry : fs::directory_iterator(toMesXmlFolder)) {
if (entry.path().extension() == ".xml") {
xmlFiles.push_back(entry.path().string());
}
}
if (xmlFiles.empty()) {
std::cout << "文件夹 " << toMesXmlFolder << " 中没有 XML 文件。" << std::endl;
return;
}
std::cout << "发现 " << xmlFiles.size() << " 个XML文件在 " << lineNumber << "线" << std::endl;
for (const auto& xmlFile : xmlFiles) {
processFile(xmlFile, uploadedFiles);
}
}
void processFile(const std::string& xmlFile, std::unordered_set<std::string>& uploadedFiles) {
try {
std::string fileName = fs::path(xmlFile).filename().string();
totalProcessed++;
if (uploadedFiles.count(fileName)) {
std::cout << "文件 " << fileName << " 已经上传过,跳过处理。" << std::endl;
return;
}
std::cout << "正在处理文件: " << fileName << std::endl;
std::string xmlContent = FileProcessor::readFile(xmlFile);
std::string response = HttpRequest::sendWithRetry(xmlContent);
std::cout << "文件 " << fileName << " 发送成功,服务器响应: " << response << std::endl;
totalSuccess++;
uploadedFiles.insert(fileName);
FileProcessor::logUploadedFile(fileName, AppConfig::uploadedFilesLog);
} catch (const std::exception& ex) {
std::cerr << "文件 " << xmlFile << " 发生错误: " << ex.what() << std::endl;
}
}
};
// 主函数
int main() {
XmlUploader uploader;
uploader.run();
return 0;
}
用C++和QT代码编译:
#include <QCoreApplication>
#include <QFile>
#include <QDir>
#include <QTextStream>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QEventLoop>
#include <QXmlStreamReader>
#include <QSet>
#include <QDateTime>
#include <QDebug>
#include <QThread>
class EMSUploader : public QObject {
Q_OBJECT
public:
explicit EMSUploader(QObject *parent = nullptr) : QObject(parent), maxRetries(3) {}
void start(const QString &baseFolderPath, const QString &apiUrl,
const QString &appId, const QString &appKey,
const QString &topic, const QString &tag, const QString &uploadedFilesLog)
{
this->baseFolderPath = baseFolderPath;
this->apiUrl = apiUrl;
this->appId = appId;
this->appKey = appKey;
this->topic = topic;
this->tag = tag;
this->uploadedFilesLog = uploadedFilesLog;
uploadedFiles = loadUploadedFiles(uploadedFilesLog);
qDebug() << "开始处理,时间:" << QDateTime::currentDateTime().toString();
processLines();
}
private:
void processLines()
{
for (int lineNumber = 1; lineNumber <= 19; ++lineNumber) {
QString lineFolder = QDir(baseFolderPath).absoluteFilePath(QString::number(lineNumber) + "线");
QString toMesXmlFolder = QDir(lineFolder).absoluteFilePath("toMesXml");
if (!QDir(toMesXmlFolder).exists()) {
qDebug() << "文件夹" << toMesXmlFolder << "不存在,跳过处理。";
continue;
}
QStringList xmlFiles = QDir(toMesXmlFolder).entryList({"*.xml"}, QDir::Files);
if (xmlFiles.isEmpty()) {
qDebug() << "文件夹" << toMesXmlFolder << "中没有XML文件。";
continue;
}
qDebug() << "发现" << xmlFiles.size() << "个XML文件在" << lineNumber << "线";
foreach (const QString &xmlFile, xmlFiles) {
QString filePath = QDir(toMesXmlFolder).absoluteFilePath(xmlFile);
processFile(filePath);
}
}
qDebug() << "处理完成,总共处理: " << totalProcessed << "个文件,成功: " << totalSuccess << "个,时间:"
<< QDateTime::currentDateTime().toString();
}
void processFile(const QString &filePath)
{
QString fileName = QFileInfo(filePath).fileName();
if (uploadedFiles.contains(fileName)) {
qDebug() << "文件" << fileName << "已经上传过,跳过处理。";
return;
}
qDebug() << "正在处理文件:" << fileName;
QString xmlContent = readXmlFile(filePath);
trySendRequestWithRetry(xmlContent, filePath);
}
QString readXmlFile(const QString &filePath)
{
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qWarning() << "无法打开XML文件:" << filePath;
return QString();
}
QTextStream in(&file);
QString content = in.readAll();
file.close();
return content;
}
void trySendRequestWithRetry(const QString &xmlContent, const QString &filePath)
{
int retryCount = 0;
while (retryCount < maxRetries) {
bool success = sendHttpRequest(xmlContent);
if (success) break;
retryCount++;
qDebug() << "请求失败,等待后进行第" << retryCount << "次重试...";
QThread::msleep(1000 * retryCount); // 注意:这会阻塞当前线程
}
if (retryCount >= maxRetries) {
qWarning() << "在" << maxRetries << "次尝试后仍然失败";
return;
}
// 假设发送成功
totalProcessed++;
totalSuccess++;
uploadedFiles.insert(QFileInfo(filePath).fileName());
appendToFile(uploadedFilesLog, QFileInfo(filePath).fileName() + "\n");
}
bool sendHttpRequest(const QString &xmlContent)
{
// 使用大括号初始化 QNetworkRequest 对象
QNetworkRequest request{QUrl(apiUrl)};
request.setRawHeader("X-HW-ID", appId.toUtf8());
request.setRawHeader("X-HW-APPKEY", appKey.toUtf8());
request.setRawHeader("MsgTopic", topic.toUtf8());
request.setRawHeader("MsgTag", tag.toUtf8());
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/xml");
QNetworkAccessManager manager;
QEventLoop loop;
QNetworkReply *reply = manager.post(request, xmlContent.toUtf8());
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
loop.exec(); // 等待请求完成
if (reply->error() == QNetworkReply::NoError) {
qDebug() << "文件发送成功,服务器响应:" << reply->readAll();
reply->deleteLater();
return true;
} else {
qDebug() << "请求错误:" << reply->errorString();
reply->deleteLater();
return false;
}
}
QSet<QString> loadUploadedFiles(const QString &logFilePath)
{
QSet<QString> files;
QFile file(logFilePath);
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QTextStream in(&file);
while (!in.atEnd()) {
files.insert(in.readLine().trimmed());
}
qDebug() << "已加载" << files.size() << "个已上传文件记录";
file.close();
} else {
qDebug() << "上传记录文件" << logFilePath << "不存在,将创建新文件";
}
return files;
}
void appendToFile(const QString &filePath, const QString &content)
{
QFile file(filePath);
if (file.open(QIODevice::Append | QIODevice::Text)) {
QTextStream out(&file);
out << content;
file.flush();
file.close();
} else {
qWarning() << "无法追加写入文件:" << filePath;
}
}
private:
QString baseFolderPath;
QString apiUrl;
QString appId;
QString appKey;
QString topic;
QString tag;
QString uploadedFilesLog;
QSet<QString> uploadedFiles;
int maxRetries;
int totalProcessed = 0;
int totalSuccess = 0;
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
EMSUploader uploader;
uploader.start("Z:/彩盒CCD", "https://apigw-cn-south.huawei.com/api/mqs/message",
"huawei.supply.mes.mdip.wh.genuine_opto", "bWe96ZRDRrxzY2YMHIS5jQ==",
"T_EMS_AOI", "HGZYXG", "uploaded_files.txt");
return a.exec();
}
#include "main.moc"
1385

被折叠的 条评论
为什么被折叠?



