[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  
<pre class="brush:c++"
class="brush:plain; gutter:true">
#pragma once  
#include <drogon/HttpController.h>  

using namespace drogon;

class FileController : public HttpController<FileController>  
{  
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<void(const HttpResponsePtr&)>&& callback);  
    void handleDownload(const HttpRequestPtr& req, std::function<void(const HttpResponsePtr&)>&& callback, const std::string& filename);  
};

</pre>
---

FileController.cc  
<pre class="brush:c++"
class="brush:plain; gutter:true">
#include "FileController.h"  
#include <drogon/utils/Utilities.h>  
#include <fstream>  

// Upload handler  
void FileController::handleUpload(const HttpRequestPtr& req, std::function<void(const HttpResponsePtr&)>&& 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<void(const HttpResponsePtr&)>&& 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);  
    }  
}  
</pre>

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`

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

---

## 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.

<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxY-7BFP1y4uLVFWRthy5eTJdZ8aBeWRUYPgt42tNiodWc-JhuSSoM3QrRdOT9uVIdU4_clXiTZnznu20qYxONKfT0nTpAS944CIUZ507letmkRTL3Yc3KU4oUbl2fR_tCk3PUISvYh4PI1jygqn1BmodjUJTJWZDI9wnVtYghWKb6UrruFAzWG9HE/s1600/upload-start.PNG" style="display: block; padding: 1em 0; text-align: center; "><img alt="drogon upload" border="0" data-original-height="386" data-original-width="560" loading="lazy" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxY-7BFP1y4uLVFWRthy5eTJdZ8aBeWRUYPgt42tNiodWc-JhuSSoM3QrRdOT9uVIdU4_clXiTZnznu20qYxONKfT0nTpAS944CIUZ507letmkRTL3Yc3KU4oUbl2fR_tCk3PUISvYh4PI1jygqn1BmodjUJTJWZDI9wnVtYghWKb6UrruFAzWG9HE/s1600-rw/upload-start.PNG"/></a></div>

---

- The server should respond with 200 OK.

<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLp6e5Cb9ooHOUoRzA2BfLiTBjTvJguVZYXdHevifJQKUnLV9GAPUKrlRn1mmVhhQ06pUQlRKCxkDv8sPK1C7CLIDXlPApcytZfmgVH-h0-8JVJiHq_eujj1nP5dy7bz7fKkef_-d_mt_JYRkfvh0boe2juHmzmoChNrRSrNkzPkfeZo6nZjzqwPmN/s1600/uploading.PNG" style="display: block; padding: 1em 0; text-align: center; "><img alt="200 ok" border="0" data-original-height="491" data-original-width="1075" loading="lazy" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLp6e5Cb9ooHOUoRzA2BfLiTBjTvJguVZYXdHevifJQKUnLV9GAPUKrlRn1mmVhhQ06pUQlRKCxkDv8sPK1C7CLIDXlPApcytZfmgVH-h0-8JVJiHq_eujj1nP5dy7bz7fKkef_-d_mt_JYRkfvh0boe2juHmzmoChNrRSrNkzPkfeZo6nZjzqwPmN/s1600-rw/uploading.PNG"/></a></div>

---

- 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 &#8220;[5] NGINX Server Setup.&#8221;

<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpJMCMbFSk1PGLklcdfmWHXcJB-UUe4ZVddSvuUSVcMlC-IbF8GXvCCs4k-UfmqSaiw_QIHzXamvSSGpm86GowVfbWwBnbDF16-T4FMMV1WEHZoq6gSu8wXFr9Xc772Wnb5mxB_mOyp5W3k-kpPO_5oVA0KgAP-I_2qZTrwqoajAHEprf2krMs94Ql/s1600/list.PNG" style="display: block; padding: 1em 0; text-align: center; "><img alt="drogon download" border="0" data-original-height="360" data-original-width="686" loading="lazy" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpJMCMbFSk1PGLklcdfmWHXcJB-UUe4ZVddSvuUSVcMlC-IbF8GXvCCs4k-UfmqSaiw_QIHzXamvSSGpm86GowVfbWwBnbDF16-T4FMMV1WEHZoq6gSu8wXFr9Xc772Wnb5mxB_mOyp5W3k-kpPO_5oVA0KgAP-I_2qZTrwqoajAHEprf2krMs94Ql/s1600-rw/list.PNG"/></a></div>

---

### 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]  
```

---

<!-- 목록을 표시할 HTML 컨테이너 -->
<div>
    <h3>Related Links</h3>
    <ul id="label-post-list">
        <!-- 여기에 게시물 목록이 추가됩니다 -->
    </ul>
</div>

---

<!-- 목록을 표시할 HTML 컨테이너 -->
<div>
    <h3>Recommended Link</h3>
    <ul id="label-post-list-include">
        <!-- 여기에 게시물 목록이 추가됩니다 -->
    </ul>
</div>

---



댓글

이 블로그의 인기 게시물

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

대통령 퇴진운동 관련주: 방송·통신·촛불수혜주 완벽 분석

키움 OPEN API MFC 개발 (1)