You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux Socket API实现HTTP服务器:图片文件传输损坏问题

HTTP服务器PNG/JPEG传输损坏,PDF正常的问题修复

我用Linux Socket API实现了带文件传输功能的HTTP服务器,目前遇到一个问题:传输PNG/JPEG图片时,写入本地的文件损坏无法打开,但PDF文件传输完全正常。

请求处理流程与代码

1. 请求接收函数

template <typename T>
void server::printReceivedData(class acceptedSocket<T> *socket)
{
    T buffer[1025];

    int acceptedSocketFD = socket->getAcceptedSocketFileDescriptor();

    while (true)
    {
        ssize_t bytesReceived = recv(acceptedSocketFD, buffer, sizeof(buffer), 0);

        if (bytesReceived <= 0)
        {
            std::cerr << "Receive failed! "
                      << socket->getError() << "\n";

            std::string file = __user->getFileInQueue();

            if (!file.empty())
            {
                 addToFileTable(file.c_str(), 0);

                __user->clearFileInQueue();

                if (send(acceptedSocketFD, "HTTP/1.1 302 Found\r\nLocation: /index.html\r\nConnection: close\r\n\r\n", 65, 0) == -1)
                    std::cerr << "Failed to send response.\n";
            }

            break;
        }

        std::cout << "\n____________________________________________________________\n\n";

        buffer[bytesReceived] = '\0';
        std::cout << buffer;

        HTTPrequestsHandler<T>(buffer, acceptedSocketFD, bytesReceived);
    }

    close(socket->getAcceptedSocketFileDescriptor());
    delete socket;
}

2. 文件写入路由函数

int user::addFilesRoute(const char *buffer, int acceptedSocketFileDescriptor, ssize_t __bytesReceived)
{
    if (findString(buffer, "filename="))
    {
        fileName.clear();
        path.clear();

        path = "interface/storage/";

        const std::string t_buffer = std::string(buffer);

        std::regex fileNameRegex(R"(filename=\"([^\"]+)\")");
        std::smatch match;

        if (std::regex_search(t_buffer, match, fileNameRegex))
            fileName = match.str(1);
        
        path = path + fileName;

        addFileInQueue(fileName);
    }

    std::ofstream file(path, std::ios::out | std::ios::app | std::ios::binary);

    if (file.is_open())
    {
        file.write(buffer, __bytesReceived);
        file.close();
    }

    return EXIT_SUCCESS;
}

问题现状

所有文件都能成功写入本地,但PNG/JPEG图片文件损坏无法打开,PDF文件正常。

补充说明

  • addFileInQueue:用于准备文件名并存入数据库的函数
  • filename:保存当前HTTP请求文件名的全局变量
  • path:指定文件保存路径的全局变量
  • 每次接收的buffer仅为1025字节的请求数据块,非完整请求
  • 尝试过移除请求头部,但直接导致功能失效

问题根源与修复方案

1. 核心问题1:HTTP头部被写入文件

HTTP文件上传使用multipart/form-data格式,请求的第一个数据块包含HTTP头部(如Content-Disposition、Content-Type)和部分文件数据,后续块是纯文件数据。当前代码把每个接收的buffer完整写入文件,导致头部信息被混入图片文件中——图片格式对内容完整性要求极高,头部冗余数据直接导致文件损坏;而PDF格式对部分冗余数据容忍度更高,所以仍能打开。

2. 核心问题2:二进制数据被篡改

代码中执行了buffer[bytesReceived] = '\0',这个操作是为了打印字符串,但会破坏二进制数据:

  • 当bytesReceived等于sizeof(buffer)时,会覆盖buffer最后一个有效字节
  • 即使未覆盖,这个操作也属于不必要的修改,可能干扰后续数据处理

3. 具体修复步骤

步骤1:移除buffer[bytesReceived] = '\0'

如果需要打印日志,单独处理字符串类型的请求头,不要修改原始接收的二进制buffer。

步骤2:准确分离HTTP头部与文件数据

需要解析multipart/form-data的边界(boundary),具体逻辑:

  1. 从请求的Content-Type头中提取boundary值(例如从Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryxxx中获取----WebKitFormBoundaryxxx)
  2. 当检测到filename=后,继续读取数据直到遇到\r\n\r\n,这之后的内容才是真正的文件数据
  3. 后续接收的buffer中,要排除末尾的boundary结束标记(如--boundary--)
  4. 仅将分离出的文件数据部分写入目标文件,而非整个buffer

步骤3:修复全局变量的线程安全问题

fileName和path是全局变量,多客户端同时上传时会被覆盖,导致文件写入错误。建议改用类成员变量或请求上下文对象传递这些数据。

步骤4:优化文件写入逻辑

不要每次接收数据都打开/关闭文件,应该在开始写入时打开文件句柄,保持到整个文件传输完成后再关闭,减少IO开销和错误概率。


内容的提问来源于stack exchange,提问作者Sorin Tudose

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 15:35:02