HTTP檔案上傳
先製作一個簡單的html頁面,只包含了一個form
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"><html> <head> test upload </head> <body> <form method="post" name="submit" enctype="multipart/form-data"> <input type="file" name="fileField"><br /><br /> <input type="submit" name="submit" value="Submit"> </form> </body></html>
form採用post方法提交. enctype的內容應該填寫MIME_type,規定from的資料發送給伺服器之前如何編碼。
這裡為了檔案上傳,必須指定multipart/form-data,也就是要求不要編碼。
預設的enctype值是application/x-www-form-urlencoded,會對字元進行編碼。
具體參考文檔:http://www.w3school.com.cn/tags/att_form_enctype.asp
MIME
因為已經涉及到MIME,這裡順便介紹一下。全稱是Multipurpose Internet Mail Extensions.
這個對mail的擴充協議允許mail message能夠支援非ASCII字元,二進位格式附件等多種格式。
它的寫法格式是:
Content-Type: [type]/[subtype]; parameter
比如multipart是 type, form-data是 subtype。
具體請參考:http://zh.wikipedia.org/wiki/MIME
HTTP協議有時候也會使用MIME,但並不完全遵循MIME。
CppCMS樣本
這個程式很簡單,upload 一個pdf檔案,然後儲存到檔案系統。我沒有使用CppCMS的Form類,因為那是用C++類渲染HTML Form以及裡面的html tags。就像JSF一樣,會把事情搞複雜。我並不欣賞這種設計。因此我用的是CppCMS的最基本的方法,從request裡擷取上傳的檔案對象,然後儲存到磁碟上。因此我的例子比CppCMS的example更好理解。
首先建立CMake工程,目錄結構如下:
chenshu@chenshu-beijing:~/work/research/upload$ tree.├── CMakeLists.txt├── config.js├── include│ ├── controller│ │ └── upload_site.h│ └── model│ └── content.h├── src│ ├── CMakeLists.txt│ ├── controller│ │ └── upload_site.cpp│ ├── main.cpp│ └── view│ └── upload.cpp└── template └── upload.tmpl7 directories, 9 files
因為CppCMS也是一個MVC架構,controller目錄中的upload_site.cpp負責:
1.負責調用渲染引擎,產生並返回網頁
2.將不同的HTTP URL映射到不同的函數
template目錄存放了要渲染的網頁模板檔案。
view目錄存放了CppCMS解析模板檔案產生的C++類
model存放了渲染網頁時需要的資料
現在看一下config.js,配置很簡單:
{ "service" : {"ip":"0.0.0.0", "api" : "http", "port" : 8081 }, "http" : { "script_names" : ["/cppcms"] }}
好,將一開始的html頁面轉成upload.tmpl檔案。
<% c++ #include "model/content.h" %><% skin upload %><% view upload_view uses content::upload_content %><% template render() %><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html> <head> test upload </head> <body> <form method="post" action="./process_uploaded_file" name="submit" enctype="multipart/form-data"> <input type="file" name="fileField"><br /><br /> <input type="submit" name="submit" value="Submit"> </form> </body></html><% end template %><% end view %><% end skin %>
model/content.h就是一個目前什麼都沒有的C++類,以便日後使用:
#ifndef CONTENT_H#define CONTENT_H#include <cppcms/view.h>namespace content { struct upload_content : public cppcms::base_content { };}#endif
現在重點看一下controller,
upload_site.h
#include <cppcms/application.h>#include <cppcms/applications_pool.h>#include <cppcms/service.h>class upload_site : public cppcms::application {public: upload_site(cppcms::service &srv); void upload_page(); void process_uploaded_file();};
upload_site.cpp
#include "controller/upload_site.h"#include <cppcms/url_dispatcher.h>#include "model/content.h"#include <cppcms/http_file.h>using namespace std;upload_site::upload_site(cppcms::service &srv):cppcms::application(srv) { //web page dispatcher().assign("/upload" , &upload_site::upload_page, this); dispatcher().assign("/process_uploaded_file" , &upload_site::process_uploaded_file, this);}void upload_site::upload_page() { content::upload_content content; render("upload","upload_view", content);}void upload_site::process_uploaded_file() { if(request().request_method()=="POST") { cppcms::http::request::files_type files = request().files(); int size = files.size(); booster::shared_ptr<cppcms::http::file> file = files.at(0); string file_name = file->filename(); file->save_to("/home/chenshu/"+file_name); }}
我的src/CMakeLIsts.txt
cmake_minimum_required(VERSION 2.8)set(CMAKE_BUILD_TYPE Debug)set(TEMPLATE_DIR ${CMAKE_SOURCE_DIR}/template)set(VIEW_DIR ${CMAKE_SOURCE_DIR}/src/view)find_package (Threads)find_package(Boost COMPONENTS system filesystem REQUIRED)include_directories(${CMAKE_SOURCE_DIR}/include)add_definitions(-Wall)add_custom_command( OUTPUT ${VIEW_DIR}/upload.cpp COMMAND cppcms_tmpl_cc ${TEMPLATE_DIR}/upload.tmpl -o ${VIEW_DIR}/upload.cpp DEPENDS ${TEMPLATE_DIR}/upload.tmpl)AUX_SOURCE_DIRECTORY(${CMAKE_SOURCE_DIR}/src CPP_LIST1)set(CPP_LIST2 ${VIEW_DIR}/upload.cpp )AUX_SOURCE_DIRECTORY(${CMAKE_SOURCE_DIR}/src/controller CPP_LIST3)add_executable(upload ${CPP_LIST1} ${CPP_LIST2} ${CPP_LIST3})target_link_libraries(upload ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} cppcms booster)
編譯運行後,通過瀏覽器訪問http://localhost:8081/cppcms/upload
然後upload一個pdf檔案。到/home/chenshu/下尋找這個pdf檔案。
我用md5sum對比了源檔案和網站儲存的檔案,md5相同。
成功。