[6] Nginx Server Setup - Implementing Upload/Download + Simple Upload Client
- 공유 링크 만들기
- X
- 이메일
- 기타 앱
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 #includeusing 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.
- The server should respond with 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.”
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]
Related Links
- [7] Nging Server Setup - Building a Text Detection API using Drogon Server with OpenCV
- [5] Nginx Server Setup - Folder Listing on Web
- [4] Nginx Server Setup - Writing a Drogon Test API
- [3] Nginx Server Setup - Connecting to C++ Web Framework
- [2] Nginx Server Setup - Downloading and Setting Up a Specific Version of Nginx
- [1] Nginx Server Setup - VirtualBox Installation and Putty Connection
Recommended Link
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--- FileController.ccusing 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); }; #include "FileController.h" #includeAfter 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`#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); } } <!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. --- - The server should respond with 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.” --- ### 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] ``` ------Related Links
---Recommended Link
- 공유 링크 만들기
- X
- 이메일
- 기타 앱
댓글
댓글 쓰기