[Webserver]httplib webserver+file upload(multipart,http)(macoOS)

πŸ‘‰πŸ» httplib μ›Ήμ„œλ²„μ—μ„œ multipart 파일 μ—…λ‘œλ“œλ₯Ό κ΅¬ν˜„ν•˜λŠ” μ„€λͺ…μž…λ‹ˆλ‹€.
This is a description of implementing multipart file uploads on an httplib web server.

πŸ‘‰πŸ» ν”„λ‘œμ νŠΈ 디렉토리내에 httplib.hνŒŒμΌμ„ λ‹€μš΄ λ°›μŠ΅λ‹ˆλ‹€.
Download the httplib.h file into the project directory.

βœ”οΈ httplib.h(0.42.0)

curl -L https://raw.githubusercontent.com/yhirose/cpp-httplib/master/httplib.h -o httplib.h

πŸ‘‰πŸ»Http server basic (upload_server.cpp)

βœ”οΈ μ•„λž˜λŠ” cpp ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ 둜컬 μ„œλ²„λ‘œ νŒŒμΌμ„ μ—…λ‘œλ“œ ν•˜λŠ” μ„€λͺ…μž…λ‹ˆλ‹€.
The following is an explanation of uploading files from a C++ client to a local server.

βœ”οΈ κΉƒν—ˆλΈŒμ—μ„œ 파일 λ‹€μš΄λ‘œλ“œμ‹œ basic 디렉토리 내에 μžˆλŠ” 파일 μž…λ‹ˆλ‹€.
This is the file located in the basic directory when downloading from GitHub.

βœ”οΈ μ½”λ“œ / Code

— upload_server.cpp

#include "../httplib.h"
#include <fstream>
#include <iostream>

int main() {
    httplib::Server svr;

    svr.Post("/upload", [&](const httplib::Request& req, httplib::Response& res,
                            const httplib::ContentReader& content_reader) {
        if (!req.is_multipart_form_data()) {
            res.status = 400;
            res.set_content("Not multipart/form-data", "text/plain");
            return;
        }

        std::string filename;
        std::ofstream ofs;

        content_reader(
            [&](const httplib::FormData& item) {
                if (item.name != "file") return true;

                filename = httplib::sanitize_filename(item.filename);
                if (filename.empty()) {
                    res.status = 400;
                    res.set_content("Invalid filename", "text/plain");
                    return false;
                }
                // file upload directory
                ofs.open("../uploads/" + filename, std::ios::binary);
                if (!ofs) {
                    res.status = 500;
                    res.set_content("Failed to open file", "text/plain");
                    return false;
                }
                return true;
            },
            [&](const char* data, size_t len) {
                if (ofs) ofs.write(data, len);
                return true;
            }
        );

        if (ofs) ofs.close();

        res.set_content("Upload OK", "text/plain");
    });

    std::cout << "Upload server listening on http://0.0.0.0:5080\n";
    svr.listen("0.0.0.0", 5080);
}

— upload_client.cpp

#include "../httplib.h"
#include <fstream>
#include <iostream>

int main() {
    httplib::Client cli("http://localhost:5080");
 
    // μ—…λ‘œλ“œ ν•­λͺ© / upload
    httplib::UploadFormData item;
    item.name = "file";
    item.filename = "test.txt";
    item.content_type = "text/plain";

    // 파일 읽기 / read file
    std::ifstream ifs("test.txt", std::ios::binary);
    if (!ifs) {
        std::cerr << "cannot open file\n";
        return 1;
    }
    item.content.assign((std::istreambuf_iterator<char>(ifs)),
                         std::istreambuf_iterator<char>());

    httplib::UploadFormDataItems items;
    items.push_back(item);

    httplib::Headers headers; 

    if (auto res = cli.Post("/upload", headers, items)) {
        std::cout << "status: " << res->status << "\n";
        std::cout << res->body << "\n";
    } else {
        std::cerr << "request failed\n";
    }
}

βœ”οΈ 컴파일 / Compiling

g++ upload_server.cpp -o upload_server -std=c++11

βœ”οΈ μ„œλ²„ μ‹€ν–‰ / Run Server

./upload_server
μ„œλ²„μ‹€ν–‰ / Run Server

βœ”οΈ 컴파일 / Compiling

g++ upload_client.cpp -o upload_client -std=c++11

βœ”οΈ ν΄λΌμ΄μ–ΈνŠΈ μ‹€ν–‰ / Run Client

./upload_client
ν΄λΌμ΄μ–ΈνŠΈ μ‹€ν–‰ / Run Client

βœ”οΈ 파일 μ—…λ‘œλ“œ μƒνƒœ 확인 / Check file upload status

파일 μ—…λ‘œλ“œ μƒνƒœ 확인 / Check file upload status

πŸ‘‰πŸ»Http server html (upload_server.cpp)

βœ”οΈ μ•„λž˜λŠ” htmlνŒŒμΌμ—μ„œ νŒŒμΌμ„ μ—…λ‘œλ“œ ν•˜λŠ” μ„€λͺ…μž…λ‹ˆλ‹€.
Below is an explanation of how to upload a file in an HTML file.

βœ”οΈ κΉƒν—ˆλΈŒμ—μ„œ 파일 λ‹€μš΄λ‘œλ“œμ‹œ uploads_html 디렉토리 내에 μžˆλŠ” 파일 μž…λ‹ˆλ‹€.
This is the file located in the uploads_html directory when downloading from GitHub.

βœ”οΈ www 디렉토리가 μ—†μœΌλ©΄ 디렉토리λ₯Ό λ§Œλ“¦λ‹ˆλ‹€.
If the www directory does not exist, create the directory.

βœ”οΈ www 디렉토리 μ•ˆμ— κ°„λ‹¨ν•œ index.htmlνŒŒμΌμ„ λ§Œλ“¦λ‹ˆλ‹€.
Create a simple index.html file inside the www directory.

βœ”οΈ index.html

<!doctype html>
<html lang="ko">
    <head>
        <meta charset="utf-8" />
        <title>File Upload</title>
    </head>
    <body>
        <h1>파일 μ—…λ‘œλ“œ/File Upload</h1>
        <form action="/upload" method="post" enctype="multipart/form-data">
            <input type="file" name="file" />
            <button type="submit">μ—…λ‘œλ“œ/Upload</button>
        </form>
    </body>
</html>

βœ”οΈupload_server.cpp

#include "../httplib.h"
#include <fstream>
#include <iostream>
#include <sstream>

int main() {
    httplib::Server svr;

    // html upload form
    if (!svr.set_mount_point("/", "./www")) {
        std::cerr << "경둜 폴더λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€! 경둜λ₯Ό ν™•μΈν•˜μ„Έμš”./The path folder cannot be found! Please check the path." << std::endl;
        return 1;
    }

    // μ—…λ‘œλ“œ 처리 / Upload processing
    svr.Post("/upload", [&](const httplib::Request& req, httplib::Response& res,
                            const httplib::ContentReader& content_reader) {
        if (!req.is_multipart_form_data()) {
            res.status = 400;
            res.set_content("Not multipart/form-data", "text/plain");
            return;
        }

        std::string filename;
        std::ofstream ofs;

        content_reader(
            [&](const httplib::FormData& item) {
                if (item.name != "file") return true;

                filename = httplib::sanitize_filename(item.filename);
                if (filename.empty()) {
                    res.status = 400;
                    res.set_content("Invalid filename", "text/plain");
                    return false;
                }

                ofs.open("../uploads/" + filename, std::ios::binary);
                if (!ofs) {
                    res.status = 500;
                    res.set_content("Failed to open file", "text/plain");
                    return false;
                }
                return true;
            },
            [&](const char* data, size_t len) {
                if (ofs) ofs.write(data, len);
                return true;
            }
        );

        if (ofs) ofs.close();
        res.set_content("Upload OK", "text/plain");
    });

    std::cout << "Server listening on http://0.0.0.0:5080\n";
    svr.listen("0.0.0.0", 5080);
}

βœ”οΈ 컴파일 / Compiling

g++ upload_server.cpp -o upload_server -std=c++11

βœ”οΈ μ„œλ²„μ‹€ν–‰ / Run Server

./upload_server

βœ”οΈ λΈŒλΌμš°μ € 접속 / Browser access

/

upload ok

파일 μ—…λ‘œλ“œ μƒνƒœ 확인 / Check file upload status

Leave a Reply