[6] Nginx Server Setup - Implementing Upload/Download + Simple Upload Client

With directory listing set up, let's implement file upload/download functionality using Drogon.


1. Server Function Implementation

1-1. Implementing Drogon Controller

Register the Drogon Controller. (This step is required for it to be recognized during the build.)

cd /root/drogon2/drogon/build/drogon_ctl  
drogon_ctl create controller FileController  

1-2. Implement Upload/Download Functionality

Source path: /root/drogon2/drogon/build/drogon_ctl/testAPI/controllers

FileController.h

#pragma once  
#include   

using namespace drogon;

class FileController : public HttpController  
{  
public:  
    std::string _storagePath = "/root/storage/";  
    METHOD_LIST_BEGIN  
    // Handles POST requests for "/upload"
    ADD_METHOD_TO(FileController::handleUpload, "/upload", Post);  

    // Handles GET requests for "/download/{filename}"
    ADD_METHOD_TO(FileController::handleDownload, "/download/{1}", Get);  
    METHOD_LIST_END  

    // Method declarations
    void handleUpload(const HttpRequestPtr& req, std::function&& callback);  
    void handleDownload(const HttpRequestPtr& req, std::function&& callback, const std::string& filename);  
};


FileController.cc

#include "FileController.h"  
#include   
#include   

// Upload handler  
void FileController::handleUpload(const HttpRequestPtr& req, std::function&& callback)  
{  
    Json::Value respStr;  
    HttpStatusCode code = k200OK;  

    MultiPartParser fileUpload;  
    do  
    {  
        if (fileUpload.parse(req) != 0)  
        {  
            code = k400BadRequest;  
            respStr = Json::Value("Multipart Format Invalid");  
            break;  
        }  

        auto &file = fileUpload.getFiles()[0];  
        LOG_INFO << "file:" << file.getFileName()  
            << " (extension=" << file.getFileExtension()  
            << ", type=" << file.getFileType()  
            << ", len=" << file.fileLength()  
            << ", md5=" << file.getMd5() << ")";  

        std::string uploadPath = _storagePath + file.getFileName();  
        LOG_INFO << "uploadPath:" << uploadPath.c_str();  

        if (file.saveAs(uploadPath) != 0)  
        {  
            code = k500InternalServerError;  
            respStr = Json::Value("Internal Server Error");  
            break;  
        }  
    } while(false);  

    auto resp = HttpResponse::newHttpResponse();  
    resp->setStatusCode(code);  
    callback(resp);  
}

// Download handler  
void FileController::handleDownload(const HttpRequestPtr& req, std::function&& callback, const std::string& filename)  
{  
    std::string filePath = _storagePath + filename;  
    if (std::ifstream(filePath).good())  
    {  
        auto resp = HttpResponse::newFileResponse(filePath.c_str(), filename.c_str());  
        resp->setContentTypeCode(CT_APPLICATION_OCTET_STREAM);  
        resp->addHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");  
        callback(resp);  
    }  
    else  
    {  
        auto resp = HttpResponse::newHttpResponse();  
        resp->setStatusCode(k404NotFound);  
        callback(resp);  
    }  
}  

After the above steps, navigate to the build directory and run make:

cd /root/drogon2/drogon/build/drogon_ctl/testAPI/build  
make  

1-3. Modify Drogon Configuration

Edit /root/drogon2/drogon/build/drogon_ctl/testAPI/config.json and modify the client_max_body_size value (default is 1M).

"client_max_body_size": "256M"  # Default is 1M, adjust as needed

If this value is not modified, a "413 Request Entity Too Large" error will be returned. With these modifications, the server setup is complete.


2. Client Function Implementation

Add an HTML file at the following path: /root/nginx/html/upload.html

<!DOCTYPE html>  
<html lang="en">  
<head>  
    <meta charset="UTF-8">  
    <title>File Upload</title>  
</head>  
<body>  
    <h2>Upload a File</h2>  
    <form action="/upload" method="post" enctype="multipart/form-data">  
        <input type="file" name="file" required>  
        <br><br>  
        <button type="submit">Send</button>  
    </form>  
</body>  
</html>  

3. NGINX Configuration

  • Adjust the API alias settings
  • Also modify client_max_body_size
user nobody;  
worker_processes  10;

events {  
    worker_connections  1024;  
    use epoll;  
}

http {  
    include       mime.types;  
    default_type  application/octet-stream;

    sendfile        on;  
    client_max_body_size 256m;

    proxy_buffer_size 128k;  
    proxy_buffers 4 256k;

    server {  
        listen       10099;  
        server_name  localhost;

        location /doUpload {  
            alias /root/nginx/html/;  
            index upload.html;  
            autoindex on;  
        }

        location /list {  
            alias /root/storage/;  
            autoindex on;                  # Enable directory listing  
            autoindex_exact_size off;      # Display file size in simplified format (optional)  
            autoindex_localtime on;        # Display file time in local time (optional)  
        }

        location /upload {  
            proxy_set_header Host $host;  
            proxy_set_header X-Real-IP $remote_addr;  
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;  
            proxy_set_header X-Forwarded-Proto $scheme;  
            proxy_pass http://127.0.0.1:8848;  # Proxy target server  
        }

        location ~ ^/download/(.*) {  
            proxy_set_header Host $host;  
            proxy_set_header X-Real-IP $remote_addr;  
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;  
            proxy_set_header X-Forwarded-Proto $scheme;  
            proxy_pass http://127.0.0.1:8848/download/$1;  # Pass captured variable to backend  
        }
    }
}

If a "413 Request Entity Too Large" error occurs, adjust client_max_body_size.


4. Verifying Operation

4-1. Verify Upload Operation

  • Call the upload API and select a file using the client.
drogon upload

  • The server should respond with 200 OK.
200 ok

  • Files are saved in the following path:
[root@vbox storage] $ ll  
Total 72  
-rw-r--r--. 1 root root 66662 Nov  4 08:48 7.PNG  
-rw-r--r--. 1 nobody nobody 2 Nov  4 06:21 test.txt  

You can also confirm this in the listing set up in “[5] NGINX Server Setup.”

drogon download

4-2. Verify Download Operation

  • In a web browser, go to http://127.0.0.1:10099/download/7.PNG to confirm file download functionality.
    (While it is possible to download from the listing page, use the API directly to test the download function.)
# Download with wget; file size should match expected size
[root@vbox ~] $ wget http://127.0.0.1:10099/download/7.PNG  
--2024-11-04 08:59:27--  http://127.0.0.1:10099/download/7.PNG  
Connecting to 127.0.0.1:10099... connected.  
HTTP request sent, awaiting response... 200 OK  
Length: 66662 (65K) [application/octet-stream]  
Saving to: `7.PNG'  

7.PNG                                   100%[=====================================================================>]  65.10K  --.-KB/s    in 0s  

2024-11-04 08:59:27 (397 MB/s) - `7.PNG' saved [66662/66662]  



댓글

이 블로그의 인기 게시물

윤석열 계엄령 선포! 방산주 대폭발? 관련주 투자 전략 완벽 분석

한국 핵무장 논의와 방위산업 관련주: 핵무기 개발 과정과 유망 종목 분석

[로스트아크] 제작 효율 최적화 위한 영지 세팅