我使用 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 字节的数据块,而不是空洞请求
感谢大家的帮助!
我设法解决了这个问题:
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;
}
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;
}