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.tmpl
7 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相同。
成功。