[6] Nginx 서버 셋업 - Up/Down 구현 + 간단한 업로드 client
- 공유 링크 만들기
- X
- 이메일
- 기타 앱
리스팅 까지 진행됫고, 업/다운을 Drogon을 통해 구현한다
1. 서버 기능 구현
1-1. Drogon Contoller 구현
Drogon Contoller 등록 진행을 한다 ( 아래 작업을 해야 빌드시 인식이 됨 )
cd /root/drogon2/drogon/build/drogon_ctl
drogon_ctl create controller FileController
1-2. 업/다운 구현
소스 위치할 경로 ( /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 // "/upload" 경로의 POST 요청 처리 ADD_METHOD_TO(FileController::handleUpload, "/upload", Post); // "/download/{filename}" 경로의 GET 요청 처리 ADD_METHOD_TO(FileController::handleDownload, "/download/{1}", Get); METHOD_LIST_END // 메서드 선언 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 // 업로드 핸들러 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); } // 다운로드 핸들러 void FileController::handleDownload(const HttpRequestPtr& req, std::function && callback, const std::string& filename) { std::string filePath = _storagePath.c_str() + filename; if ( std::ifstream(filePath).good() == true ) { 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); } }
작업 이후 아래 경로로 이동후 make
cd /root/drogon2/drogon/build/drogon_ctl/testAPI/build
make
1-3. drogon 설정 수정
/root/drogon2/drogon/build/drogon_ctl/testAPI/config.json 파일의
client_max_body_size
값을 수정한다 ( 기본 1M으로 되어있음 )
"client_max_body_size": "256M" # 기본 1M, 원하는 만큼 변경하면된다
위 값 수정하지않으면 413 Request Entity Too Large 에러를 리턴한다
이건 로그도 안찍어서 소스 뒤져봐야되는데, 머리아프다
위 작업 까지 완료되면 서버쪽은 완료된다
2. 클라이언트 기능 구현
아래 경로에 html 추가 /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>
3. nginx 설정
- API Alias 설정 수정
- 마찬가지로 client_body 수정
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; # 디렉터리 인덱스 표시
autoindex_exact_size off; # 파일 크기를 간소화된 형식으로 표시 (선택사항)
autoindex_localtime on; # 로컬 시간으로 파일 시간 표시 (선택사항)
}
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; # 프록시할 대상 서버
}
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; # 캡처된 변수를 백엔드 서버로 전달
}
}
}
413 Request Entity Too Large 에러 리턴되면 clientmaxbody_size 만져보시길
4. 동작 확인
4-1. upload 동작 확인
- upload api 호출 및 clinet파일 선택
- 동작, 200ok return
- 아래 경로에 저장된다
[root@vbox storage] $ ll
합계 72
-rw-r--r--. 1 root root 66662 11월 4 08:48 7.PNG
-rw-r--r--. 1 nobody nobody 2 11월 4 06:21 test.txt
- 이전 '[5] Nginx 서버 셋업' 에서 만든 리스트로 보아도 이상없다.
4-2. download 동작 확인
- 웹에서 http://127.0.0.1:10099/download/7.PNG 호출 시 다운로드 발생까지 확인 하면 된다. ( 사실 리스트에서 클릭해도 다운로드 되긴하는데, 만든 기능 확인을 위해 API로 해보자 )
# wget으로 다운로드 받아보면 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]
리스팅 까지 진행됫고, 업/다운을 Drogon을 통해 구현한다 --- ## 1. 서버 기능 구현 ### 1-1. Drogon Contoller 구현 Drogon Contoller 등록 진행을 한다 ( 아래 작업을 해야 빌드시 인식이 됨 ) ``` cd /root/drogon2/drogon/build/drogon_ctl drogon_ctl create controller FileController ``` --- ### 1-2. 업/다운 구현 소스 위치할 경로 ( /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 // "/upload" 경로의 POST 요청 처리 ADD_METHOD_TO(FileController::handleUpload, "/upload", Post); // "/download/{filename}" 경로의 GET 요청 처리 ADD_METHOD_TO(FileController::handleDownload, "/download/{1}", Get); METHOD_LIST_END // 메서드 선언 void handleUpload(const HttpRequestPtr& req, std::function && callback); void handleDownload(const HttpRequestPtr& req, std::function && callback, const std::string& filename); }; #include "FileController.h" #include작업 이후 아래 경로로 이동후 make ``` cd /root/drogon2/drogon/build/drogon_ctl/testAPI/build make ``` --- ### 1-3. drogon 설정 수정 /root/drogon2/drogon/build/drogon_ctl/testAPI/config.json 파일의 `client_max_body_size` 값을 수정한다 ( 기본 1M으로 되어있음 ) ``` "client_max_body_size": "256M" # 기본 1M, 원하는 만큼 변경하면된다 ``` 위 값 수정하지않으면 413 Request Entity Too Large 에러를 리턴한다 이건 로그도 안찍어서 소스 뒤져봐야되는데, 머리아프다 위 작업 까지 완료되면 서버쪽은 완료된다 --- ## 2. 클라이언트 기능 구현 아래 경로에 html 추가 /root/nginx/html/upload.html#include // 업로드 핸들러 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); } // 다운로드 핸들러 void FileController::handleDownload(const HttpRequestPtr& req, std::function && callback, const std::string& filename) { std::string filePath = _storagePath.c_str() + filename; if ( std::ifstream(filePath).good() == true ) { 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>