• 利用CGI (C)及HTML实现PC本地文件的上传功能


    背景:设备端(采集端) + 云端(服务端)+ 浏览端(客户端)这种结构是比较常见的,而在某些情形,云端如果涉及到跨境跨域的问题,上述的架构就不一定合适。在功能要求不是那么复杂的情况下,可以考虑设备端(采集端+服务端)+ 浏览端(客户端)这种架构。在设备端移植搭建服务器,并将显示的相关html文件部署其上,浏览器输入设备地址及html文件名,即可实现远程监控功能。

    功能:点击网页上文件选择按钮,从本地选择待上传的文件,完成后点击上传按钮,将选中的文件上传到web server。

    HTML内容如下

    例子一:

    1. html>
    2. <html>
    3. <head>
    4. <title>文件上传title>
    5. head>
    6. <body>
    7. <form action="/cgi-bin/upload.cgi" enctype="multipart/form-data" method="post">
    8. <table>
    9. <tbody>
    10. <tr>
    11. <td> upload file td>
    12. <td><input type="file" name="uploadfile" value="">td>
    13. <td><input type="submit" name="upload" value="上传">td>
    14. tr>
    15. tbody>
    16. table>
    17. form>
    18. body>
    19. html>

    例子二(网友方案):

    1. html>
    2. <html>
    3. <style>
    4. body
    5. {
    6. background-color: lightblue;
    7. }
    8. div
    9. {
    10. margin-left: 30px;
    11. margin-top: 30px;
    12. }
    13. style>
    14. <head>
    15. <meta charset="utf-8">
    16. <title>Upload File Testtitle>
    17. head>
    18. <body>
    19. <form enctype='multipart/form-data' action="/cgi-bin/upload.cgi" method="post">
    20. <div>
    21. <span>上传路径:span><input type="text" name="updatapath" value="/tmp/">
    22. div>
    23. <div>
    24. <input type="file" name='updatafile' multiple>
    25. div>
    26. <div>
    27. <input type="submit" value="确认上传">
    28. div>
    29. body>
    30. html>

    上传文件功能必须开启

    enctype="multipart/form-data" method="post"

    CGI C实现:

    例子一配套C代码:

    1. #include
    2. #include
    3. #include
    4. #include "cgic.h"
    5. enum ErrLog
    6. {
    7. ErrSucceed,
    8. ErrOpenFile,
    9. ErrNoFile
    10. };
    11. enum ErrLog UploadFile()
    12. {
    13. cgiFilePtr file;
    14. FILE *fd;
    15. char name[512];
    16. char path[128];
    17. char contentType[1024];
    18. int size = 0;
    19. int got = 0;
    20. int t = 0;
    21. char *tmp = NULL;
    22. cgiHeaderContentType("text/html; charset=utf-8");
    23. cgiFormResultType ret;
    24. ret = cgiFormFileName("uploadfile", name, sizeof(name));
    25. switch(ret)
    26. {
    27. case cgiFormNotFound:
    28. printf("

      cgiFormNotFound.

      \n"
      );
    29. break;
    30. case cgiFormTruncated:
    31. printf("

      cgiFormTruncated.

      \n"
      );
    32. break;
    33. case cgiFormSuccess:
    34. printf("

      cgiFormSuccess.

      \n"
      );
    35. break;
    36. default:
    37. printf("

      unknown.

      \n"
      );
    38. break;
    39. }
    40. if (ret != cgiFormSuccess) //获取客户端pathname
    41. {
    42. printf("

      %s No file was uploaded.

      \n"
      , name);
    43. return ErrNoFile;
    44. }
    45. fprintf(cgiOut, "The filename submitted was: ");
    46. cgiHtmlEscape(name);
    47. fprintf(cgiOut, "
      \n"
      );
    48. cgiFormFileSize("uploadfile", &size);
    49. fprintf(cgiOut, "The file size was: %d bytes
      \n"
      , size);
    50. cgiFormFileContentType("uploadfile", contentType, sizeof(contentType));
    51. fprintf(cgiOut, "The alleged content type of the file was: ");
    52. cgiHtmlEscape(contentType);
    53. fprintf(cgiOut, "
      \n"
      );
    54. if (cgiFormFileOpen("uploadfile", &file) != cgiFormSuccess) //尝试打开上传的,并存放在系统中的临时文件
    55. {
    56. fprintf(cgiOut, "

      Could not open the file.

      \n"
      );
    57. return ErrOpenFile;
    58. }
    59. t = -1;
    60. while (1)
    61. {
    62. tmp = strstr(name+t+1, "\\"); // 从pathname解析出filename
    63. if (NULL == tmp)
    64. {
    65. tmp = strstr(name+t+1, "/");
    66. }
    67. if (NULL != tmp)
    68. {
    69. t = (int)(tmp-name);
    70. }
    71. else
    72. {
    73. break;
    74. }
    75. }
    76. tmp = (char *)malloc(size * sizeof(char)); // 在底层建立新文件
    77. strcpy(path, "/tmp/"); // 路径最后一个字符必须是'/',否则最终的文件(带路径)将会不知道写到什么地方去了
    78. strcat(path, name+t+1);
    79. fd = fopen(path, "wb");
    80. if (fd == NULL)
    81. {
    82. return ErrOpenFile;
    83. }
    84. while (cgiFormFileRead(file, tmp, size, &got) == cgiFormSuccess) // 从临时文件读出content
    85. {
    86. fwrite(tmp, size, sizeof(char), fd); //把读出的content写入新文件
    87. }
    88. fprintf(cgiOut, "

      Upload File Success.

      \n"
      );
    89. cgiFormFileClose(file);
    90. free(tmp);
    91. fclose(fd);
    92. return ErrSucceed;
    93. }
    94. int cgiMain()
    95. {
    96. return UploadFile();
    97. }

    例子二配套C代码(网友方案,借鉴于CGI提供的cgitest.c例子,里面涉及到CGI的各种特性):

    1. /* Change this if the SERVER_NAME environment variable does not report
    2. the true name of your web server. */
    3. #if 1
    4. #define SERVER_NAME cgiServerName
    5. #endif
    6. #if 0
    7. #define SERVER_NAME "www.boutell.dev"
    8. #endif
    9. /* You may need to change this, particularly under Windows;
    10. it is a reasonable guess as to an acceptable place to
    11. store a saved environment in order to test that feature.
    12. If that feature is not important to you, you needn't
    13. concern yourself with this. */
    14. #include
    15. #include
    16. #include
    17. #include
    18. #include
    19. #include
    20. #include
    21. #include
    22. #include "cgic.h"
    23. //#define DEBUG_ON
    24. #ifdef DEBUG_ON
    25. void PrintMessage(const char *str)
    26. {
    27. int fd;
    28. fd = open("/home/vmuser/mycgi_log",O_WRONLY|O_CREAT|O_APPEND);
    29. if(fd < 0)
    30. return;
    31. time_t the_time;
    32. struct tm *info;
    33. time(&the_time);
    34. info = gmtime(&the_time );
    35. dprintf(fd,"[%2d:%02d]\n", (info->tm_hour)%24, info->tm_min);
    36. write(fd,str,strlen(str));
    37. close(fd);
    38. }
    39. #endif
    40. enum ErrLog
    41. {
    42. ErrSucceed = 0x00,
    43. ErrOpenFile,
    44. ErrNoFile,
    45. ErrNonePath
    46. };
    47. int cgiMain() {
    48. cgiFilePtr file;
    49. FILE *fd;
    50. char name[512];
    51. char path[128];
    52. char contentType[1024];
    53. int size = 0;
    54. int got = 0;
    55. int t = 0;
    56. char *tmp = NULL;
    57. //设置类型文件
    58. cgiHeaderContentType("text/html; charset=utf-8");
    59. if (cgiFormFileName("updatafile", name, sizeof(name)) != cgiFormSuccess) //获取客户端pathname
    60. {
    61. fprintf(cgiOut,"

      文件上传失败.

      \n"
      );
    62. return ErrNoFile;
    63. }
    64. //显示上传文件内容
    65. fprintf(cgiOut, "提交上传文件名称: ");
    66. cgiHtmlEscape(name);//虽然已经获取到名称,如果文件名中有特殊的名称,将会被转换,总结:从html获取的字符串需要显示到网页的用这个比较好,用fprintf也可以。
    67. fprintf(cgiOut, "
      \n"
      );
    68. //获取文件大小
    69. cgiFormFileSize("updatafile", &size);
    70. fprintf(cgiOut, "文件大小为: %d 字节
      \n"
      , size);
    71. //上传文件内容类型
    72. cgiFormFileContentType("updatafile", contentType, sizeof(contentType));
    73. fprintf(cgiOut, "文件的内容类型为: ");
    74. cgiHtmlEscape(contentType);
    75. fprintf(cgiOut, "
      \n"
      );
    76. if (cgiFormString("updatapath", path, sizeof (path)) != cgiFormSuccess)
    77. {
    78. fprintf(cgiOut, "

      Could not open the file.

      \n"
      );
    79. return ErrNonePath;
    80. }
    81. //上传文件内容类型
    82. fprintf(cgiOut, "文件的路径: ");
    83. cgiHtmlEscape(path);
    84. fprintf(cgiOut, "
      \n"
      );
    85. //尝试打开上传的,并存放在系统中的临时文件
    86. if (cgiFormFileOpen("updatafile", &file) != cgiFormSuccess)
    87. {
    88. fprintf(cgiOut, "

      Could not open the file.

      \n"
      );
    89. return ErrOpenFile;
    90. }
    91. t = -1;
    92. while (1)
    93. {
    94. tmp = strstr(name+t+1, "\\"); // 从pathname解析出filename
    95. if (NULL == tmp)
    96. tmp = strstr(name+t+1, "/");
    97. if (NULL != tmp)
    98. t = (int)(tmp-name);
    99. else
    100. break;
    101. }
    102. //动态内存分配
    103. tmp = (char *)malloc(size * sizeof(char));
    104. strcat(path, name+t+1);
    105. //上传文件内容类型
    106. fprintf(cgiOut, "最终生成文件: ");
    107. cgiHtmlEscape(path);
    108. fprintf(cgiOut, "
      \n"
      );
    109. //创建文件,以字节流的方式打开
    110. fd = fopen(path, "wb+");
    111. if (fd == NULL)
    112. {
    113. return ErrOpenFile;
    114. }
    115. // 从临时文件读出content
    116. while (cgiFormFileRead(file, tmp, size, &got) == cgiFormSuccess)
    117. {
    118. fwrite(tmp, size, sizeof(char), fd); //把读出的content写入新文件
    119. }
    120. //打印输出
    121. fprintf(cgiOut, "

      上传文件成功.

      \n"
      );
    122. //关闭文件
    123. cgiFormFileClose(file);
    124. free(tmp);
    125. fclose(fd);
    126. //跳转回到主页面,这个需要浏览器html代码功能来实现
    127. //fprintf(cgiOut,"");
    128. return ErrSucceed;
    129. }

            上述CGI程序的编译需要注意一点,一般有cgiMain(及其他CGI的API)需要链接静态库(或直接带上cgic.c,同样性质),比如最终编译生成的目标文件为upload.cgi,则可按照下面的语句进行编译:

    1. 交叉编译工具链按实际使用的来,下述只是示例。
    2. 方式一:
    3. arm-linux-gcc -o upload.cgi upload.c -L ./-lcgic
    4. 方式二:
    5. arm-linux-gcc -o upload.cgi upload.c cgic.c

            前期曾尝试直接通过html + js实现文件的上传功能,以期省点空间,无奈出现过4xx、3xx错误,解决了后还是没有上传成功,有知晓的同学还望指点一二。

  • 相关阅读:
    Java架构师设计思想
    VMWare16的安装及VMware配置Ubuntu虚拟机
    计网面试题
    媒介盒子:品牌宣传的内容输出逻辑是什么
    ERP从内部集成起步总结
    【漏洞复现】E-office文件包含漏洞
    七、计算机视觉-图像的ROI区域
    LeetCode59-螺旋矩阵II
    facade(门面模式或外观模式)
    2023年java代做题目参考整理
  • 原文地址:https://blog.csdn.net/DIANZI520SUA/article/details/133293716