chapter 09-01

網頁端檔案上傳

此章節說明如何處理從網頁端上傳檔案.


分成 [網頁端] 和 [CGI 端] 說明.


網頁端

網頁的編碼格式必須是 UTF-8, 如下的宣告 :
<meta http-equiv="content-type" content="text/html; charset=utf-8">


需要的 JS 檔 :
jquery_main.js
jquery_form.js
mcm_jslib_api.js


需要使用 form 處理.


步驟-01 : 加入 form 內容.
格式 :
<form id="$(form_id)">
  <input type="hidden" name="callback" value="$(function_name)">
  <input type="file" name="$(element_name)">
</form>


$(form_id)
設定 form 的 id, 上傳時需要指定要上傳的 form.

<input type="hidden" name="callback" value="$(function_name)">
需要在 CGI 模組撰寫對應的的處理函式, 需要指定處理的函式.
callback
固定的元素名稱, 表示此元素用來指定關鍵字.
$(function_name)
指定處理的處理函式的名稱.

$(element_name)
元素的名稱, CGI 端處理函式才知道這組資料的用途.

注意事項 :
01.  form 內只可以有一個指定處理函式的元素.
02.  form 內必須至少有一個 file 類型的元素.
03.  form 內可以設定多個 input 元素, 不限定類型.


步驟-02 : 在 JavaScript 內上傳 form.
變數 :
[mcm_upload_jslib_t this_upload_jslib]
用來儲存相關的的資訊.
會用到的結構成員 :
[form_id_string]
要上傳的 form 的 id. (字串格式) [詳細]
[other_query]
額外的查詢參數, 不需要使用的話填入空字串.
函式 :
mcm_jslib_upload
上傳 form 資料.
參數 說明
this_upload_jslib 用來儲存相關的的資訊
需要先設定以下的結構成員 :
form_id_string [詳細]
other_query [詳細]
回傳 說明
$.rep_code 處理結果 >= mcm_return_code.pass 成功
 < mcm_return_code.pass 錯誤
$.rep_data CGI 端填入的自定格式回應訊息


範例 :
<html>
<head>
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<script language="javascript" type="text/javascript" src="jquery_main.js"></script>
<script language="javascript" type="text/javascript" src="jquery_form.js"></script>
<script language="javascript" type="text/javascript" src="mcm_jslib_api.js"></script>
<script type="text/javascript">
function upload_file()
{
    var self_upload_jslib, rep_ret;

    if($("#input_file_upload").val() != "")
    {
        self_upload_jslib = new mcm_upload_jslib_t();
        self_upload_jslib.form_id_string = "form_custom";
        self_upload_jslib.other_query = "";
        rep_ret = mcm_jslib_upload(self_upload_jslib);
        if(rep_ret.rep_code < mcm_return_code.pass)
        {
            alert("call mcm_jslib_upload() fail" +
                  "[" + rep_ret.rep_code + "]");
            mcm_jslib_run_script(rep_ret.rep_data);
            return;
        }
        mcm_jslib_run_script(rep_ret.rep_data);
    }
}
</script>
</head>
<body>
  <form id="form_custom">
    <input type="hidden" name="callback" value="upload_handle">
    <input type="file" name="input_file_upload" id="input_file_upload">
  </form>
  <button type="button" onclick="upload_file()">upload</button>
</body>
</html>



CGI 端

檔案上傳後, 網頁伺服器會呼叫 CGI 處理, 需要在 CGI 端加入自訂的處理函式.

程式要放在 mint_cm/mcm_cgi/mcm_cgi_upload_module
沒有限制函式的放置方式, 可以依據不同的功能將各種函式分類放在不同的檔案.


步驟-01 : 加入基本的標頭檔 :
#include <stdio.h>
#include "mcm_lib/mcm_lheader/mcm_return.h"
#include "../mcm_cgi_common_extern.h"
#include "mcm_cgi_module_debug.h"

如果需要使用 MintCM 函式則改加入 (以及在 Makefile 中加入鏈結 MintCM 的函式庫) :
#include <stdio.h>
#include "mcm_lib/mcm_lheader/mcm_type.h"
#include "mcm_lib/mcm_lheader/mcm_size.h"
#include "mcm_lib/mcm_lheader/mcm_connect.h"
#include "mcm_lib/mcm_lheader/mcm_return.h"
#include "mcm_lib/mcm_lheader/mcm_data_exinfo_auto.h"
#include "mcm_lib/mcm_lulib/mcm_lulib_api.h"
#include "../mcm_cgi_common_extern.h"
#include "mcm_cgi_module_debug.h"



步驟-02 : 加入處函式.
函式格式 :
void $(function_name)(
    struct part_info_t *part_list)
{
    ...
}


$(function_name)
自定義的函式名稱.

參數的用途 :
參數 說明
struct part_info_t *
part_list
上傳的 form 裡面的每一個 input 元素內容, 雙向串列結構
(紀錄 callback 的元素不會加入到此表) [詳細]

[struct part_info_t *part_list]
結構成員
char *
name_tag
元素的 name [詳細]
char *
filename_tag
上傳的檔案的檔案名稱
必須是 file 類型的元素而且有選擇檔案上傳才會有值
char *
data_con
元素的 value 或上傳的檔案的資料內容
MCM_DTYPE_USIZE_TD
data_len
資料的長度
struct part_info_t *
prev_part
前一個節點
struct part_info_t *
next_part
後一個節點


步驟-03 : 在處理的函式內將處理結果回應給瀏覽器.
函式 :
mcm_cgi_fill_response_header
填充回應資料的開頭部分.
參數 說明
MCM_DTYPE_BOOL_TD
fill_content_text
是否在回應資料的 HTTP header 中填入
Content-Type: text/plain
0 : 否
1 : 是
MCM_DTYPE_BOOL_TD
fill_connection_close
是否在回應資料的 HTTP header 中填入
Connection: close
0 : 否
1 : 是
int
ret_code
回應資料的代碼部分
(mcm_jslib_upload() 的 $.rep_code)

注意事項 :
01.  mcm_cgi_fill_response_header() 只會填充開頭部分的資料.
02.  主要回應資料 (mcm_jslib_upload() 的 $.rep_data) 直接使用 printf() 填充.
03.  至少要用 mcm_cgi_fill_response_header() 回應處理結果, 主要回應資料可有可無.


其他 : 在 console 顯示除錯訊息.
在 CGI 使用 printf() 會被網頁伺服器導向輸出給瀏覽器, 訊息要寫入 console 設備才能顯示.

在處理函式的開頭加入打開 console 設備 :
#if MCM_CUMEMODE | MCM_CUMDMODE
    dbg_console_fd = open(MCM_DBG_CONSOLE, O_WRONLY);
    if(dbg_console_fd == -1)
        return;
#endif

在離開處理函式時加入關閉 console 設備 :
#if MCM_CUMEMODE | MCM_CUMDMODE
    close(dbg_console_fd);
#endif


[MCM_CUMEMODE]
定義, 是否要顯示錯誤訊息.
開關的值的定義在 mint_cm/mcm_lib/mcm_lheader/mcm_debug.h.

[MCM_CUMDMODE]
定義, 是否要顯示除錯訊息.
開關的值的定義在 mint_cm/mcm_lib/mcm_lheader/mcm_debug.h.

[MCM_DBG_CONSOLE]
定義, console 設備的路徑.
路徑的值定義在 mint_cm/mcm_lib/mcm_lheader/mcm_debug.h.

[int dbg_console_fd]
變數, 紀錄開啟的設備的檔案編號.
變數的宣告定義在 mcm_cgi_module_debug.h.

[char dbg_msg_buf[MCM_DBG_BUFFER_SIZE]]
變數, 儲存訊息的緩衝.
變數的宣告定義在 mcm_cgi_module_debug.h.
緩衝大小的定義在 mint_cm/mcm_lib/mcm_lheader/mcm_debug.h.

[MCM_CUMEMSG(msg_fmt, msg_args...)]
巨集, 顯示訊息 (錯誤類), 用法同 printf().
巨集的定義在 mcm_cgi_module_debug.h.

[MCM_CUMDMSG(msg_fmt, msg_args...)]
巨集, 顯示訊息 (除錯類), 用法同 printf().
巨集的定義在 mcm_cgi_module_debug.h.


範例 :
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include "mcm_lib/mcm_lheader/mcm_return.h"
#include "../mcm_cgi_common_extern.h"
#include "mcm_cgi_module_debug.h"

void upload_handle(
    struct part_info_t *part_list)
{
    struct part_info_t *tmp_part;

#if MCM_CUMEMODE | MCM_CUMDMODE
    dbg_console_fd = open(MCM_DBG_CONSOLE, O_WRONLY);
    if(dbg_console_fd == -1)
        return;
#endif

    mcm_cgi_fill_response_header(1, 1, MCM_RCODE_PASS);

    printf("alert(");
    for(tmp_part = part_list; tmp_part != NULL; tmp_part = tmp_part->next_part)
    {
        MCM_CUMDMSG("name = %s", tmp_part->name_tag);
        MCM_CUMDMSG("filename = %s", tmp_part->filename_tag);
        MCM_CUMDMSG("data = [" MCM_DTYPE_USIZE_PF "][%p]",
                    tmp_part->data_len, tmp_part->data_con);

        printf("\"name = %s\\n\" + ", tmp_part->name_tag);
        printf("\"filename = %s\\n\" + ", tmp_part->filename_tag);
        printf("\"data = [" MCM_DTYPE_USIZE_PF "][%p]\"",
               tmp_part->data_len, tmp_part->data_con);
        if(tmp_part->next_part != NULL)
            printf(" + \"\\n\" + \"\\n\" + ");
    }
    printf(");\n");

#if MCM_CUMEMODE | MCM_CUMDMODE
    close(dbg_console_fd);
#endif
    return;
}



範例程式的使用

01.  範例程式目錄在 mint_cm/usage/example/0901.


02.  下面關於 make 的操作沒有特別註明的話都是在 mint_cm 目錄.


03.  第一次使用, 使用 make example_add KEY=0901 載入範例並編譯.


04.  web_app 是範例程式.

範例項目 :
case-01 測試單個 file 元素
case-02 測試單個 file 元素加上單個其他類型的 input
case-03 測試多個 file 元素加上多個其他類型的 input
case-04 測試單個 file 元素同時上傳多個檔案
case-05 測試單個 file 元素加上單個其他類型的 input, 並在 CGI 端搭配資料存取


05.  先執行 mcm_daemon 和 mini_httpd 才可測試.


06.  瀏覽器連至 http://<server-address>/web_app_0901_index.html 就可以看到範例頁面.


07.  測試完畢不使用後, 使用 make example_del KEY=0901 將範例移除.


08.  範例程式目錄下的檔案在做完 make example_add 後會複製到真正使用的位置, 要修改做測試的話要改在複製後的.
來源 profile/mcm_data_profile_0901.xml
目地 mint_cm/mcm_build/mcm_data_profile.xml
資料模型範例
有修改要使用 make all 重新編譯
來源 profile/mcm_store_profile_default_0901.txt
目地 mint_cm/mcm_build/mcm_store_profile_default.txt
資料預設值範例
使用 make all 後會再複製到 mint_cm/run
來源 web_app
目地 mint_cm/run/web
網頁程式範例
來源 module/mcm_module_0901.c
目地 mint_cm/mcm_daemon/mcm_module
內部模組範例
有修改要使用 make all 重新編譯
來源 cgi/mcm_cgi_upload_custom.c
目地 mint_cm/mcm_cgi
CGI 端處理檔案上傳範例
有修改要使用 make all 重新編譯