处理 HTTP 请求中的多部分/表单数据

问题描述 投票:0回答:1

我使用 Linux 套接字 API 实现了一个 HTTP 服务器,我创建的功能之一是文件传输。

我遇到的问题是当我尝试传输 png/jpeg 文件时。写入二进制文件数据后,文件已损坏且无法打开。对于例如 PDF。它工作完美。

这是我处理HTTP请求的方式:

首先我使用以下函数收到请求。这里我将收到的缓冲区传递给 HTTPrequestsHandler:

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;
}

在 HTTPrequestHandle 函数中,我将缓冲区转发到正确的路由函数(在本例中为 /addFileRoute):

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;
}

这里我将文件数据写入具有正确文件名的文件中。

我添加的所有文件都已成功写入我的计算机上的文件,但图像文件无法打开。

addFileInQueue = 准备要推入数据库的文件名的函数

filename = 保存当前 HTTP 请求的文件名的全局变量

path = 用于将文件保存在正确位置的全局变量

我尝试了不同的方法来格式化文件数据。一种是删除请求中额外的所有标头,但这会破坏所有内容。

!!!请记住,缓冲区具有 1025 字节的数据块,而不是空洞请求

感谢大家的帮助!

html c++ http httprequest multipartform-data
1个回答
0
投票

我设法解决了这个问题:

  1. 我修改了 addFileroute 函数以直接将数据写入 uint8_t 类型(我将删除 char *buffer 并在本地重铸它)
int user::addFilesRoute(const char *buffer, const uint8_t *byteBuffer, int acceptedSocketFileDescriptor, ssize_t __bytesReceived)
{
    if (findString(buffer, "filename="))
    {
        fileName.clear();

        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);

        addFileInQueue(fileName);
    }

    FILE *file = fopen("interface/storage/temp.bin", "ab");

    if (file != NULL)
    {
        fwrite(byteBuffer, sizeof(uint8_t), __bytesReceived, file);
        fclose(file);
    }
    else
    {
        std::cerr << "Failed to open file!\n";
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}
  1. 我将数据写入 temp.bin 文件,收到请求后我对其进行处理。 (最后我删除了 temp.bin 文件)
int server::formatFile(const std::string fileName)
{
    std::ifstream file("interface/storage/temp.bin", std::ios::binary);
    std::ofstream outFile("interface/storage/" + fileName, std::ios::binary);

    if (!file.is_open() || !outFile.is_open())
    {
        std::cout << "Error opening files!" << std::endl;
        return EXIT_FAILURE;
    }

    std::string line;
    std::string boundary = "------WebKitFormBoundary";
    std::string contentStart = "Content-Type:";

    bool foundBoundary = false;

    while (std::getline(file, line))
    {
        if (!foundBoundary && line.find(boundary) != std::string::npos)
        {
            foundBoundary = true;
            continue;
        }

        if (foundBoundary)
            if (line.find(contentStart) != std::string::npos)
            {
                std::getline(file, line);
                
                while (std::getline(file, line))
                {

                    if (line.find(boundary) != std::string::npos)
                        break;

                    outFile << line << std::endl;
                }

                break;
            }
    }

    file.close();
    outFile.close();

    if (remove("interface/storage/temp.bin") != 0)
        std::cerr << "Failed to removed temp.bin!\n";

    return EXIT_SUCCESS;
}
© www.soinside.com 2019 - 2024. All rights reserved.