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

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

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

HTML内容如下

例子一:




    文件上传


    
upload file

例子二(网友方案):



	
    
		
        Upload File Test
    
	
    
        
上传路径:

上传文件功能必须开启

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

CGI C实现:

例子一配套C代码:

#include 
#include 
#include 
#include "cgic.h"

enum ErrLog
{
    ErrSucceed,
    ErrOpenFile,
    ErrNoFile
};
enum ErrLog UploadFile()
{
    cgiFilePtr file;
    FILE *fd;
    char name[512];
    char path[128];
    char contentType[1024];
    int size = 0;
    int got = 0;
    int t = 0;
    char *tmp = NULL;
 
	cgiHeaderContentType("text/html; charset=utf-8");
	
	cgiFormResultType ret;
	ret = cgiFormFileName("uploadfile", name, sizeof(name));
	switch(ret)
	{
		case cgiFormNotFound:
			printf("

cgiFormNotFound.

\n"); break; case cgiFormTruncated: printf("

cgiFormTruncated.

\n"); break; case cgiFormSuccess: printf("

cgiFormSuccess.

\n"); break; default: printf("

unknown.

\n"); break; } if (ret != cgiFormSuccess) //获取客户端pathname { printf("

%s No file was uploaded.

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

Could not open the file.

\n"); return ErrOpenFile; } t = -1; while (1) { tmp = strstr(name+t+1, "\\"); // 从pathname解析出filename if (NULL == tmp) { tmp = strstr(name+t+1, "/"); } if (NULL != tmp) { t = (int)(tmp-name); } else { break; } } tmp = (char *)malloc(size * sizeof(char)); // 在底层建立新文件 strcpy(path, "/tmp/"); // 路径最后一个字符必须是'/',否则最终的文件(带路径)将会不知道写到什么地方去了 strcat(path, name+t+1); fd = fopen(path, "wb"); if (fd == NULL) { return ErrOpenFile; } while (cgiFormFileRead(file, tmp, size, &got) == cgiFormSuccess) // 从临时文件读出content { fwrite(tmp, size, sizeof(char), fd); //把读出的content写入新文件 } fprintf(cgiOut, "

Upload File Success.

\n"); cgiFormFileClose(file); free(tmp); fclose(fd); return ErrSucceed; } int cgiMain() { return UploadFile(); }

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

/* Change this if the SERVER_NAME environment variable does not report
	the true name of your web server. */
#if 1
#define SERVER_NAME cgiServerName
#endif
#if 0
#define SERVER_NAME "www.boutell.dev"
#endif

/* You may need to change this, particularly under Windows;
	it is a reasonable guess as to an acceptable place to
	store a saved environment in order to test that feature. 
	If that feature is not important to you, you needn't
	concern yourself with this. */

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "cgic.h"

//#define DEBUG_ON
#ifdef DEBUG_ON
void PrintMessage(const char *str)
{
    int fd;
    fd =  open("/home/vmuser/mycgi_log",O_WRONLY|O_CREAT|O_APPEND);
    if(fd < 0)
        return;

    time_t the_time;

    struct tm *info;
    time(&the_time);
    info = gmtime(&the_time );

    dprintf(fd,"[%2d:%02d]\n", (info->tm_hour)%24, info->tm_min);

    write(fd,str,strlen(str));

    close(fd);

}
#endif


enum ErrLog
{
    ErrSucceed = 0x00,
    ErrOpenFile,
    ErrNoFile,
    ErrNonePath
};

int cgiMain() {

        cgiFilePtr file;
        FILE *fd;
        char name[512];
        char path[128];
        char contentType[1024];
        int size = 0;
        int got = 0;
        int t = 0;
        char *tmp = NULL;

        //设置类型文件
        cgiHeaderContentType("text/html; charset=utf-8");
        if (cgiFormFileName("updatafile", name, sizeof(name)) != cgiFormSuccess) //获取客户端pathname
        {
           fprintf(cgiOut,"

文件上传失败.

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

Could not open the file.

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

Could not open the file.

\n"); return ErrOpenFile; } t = -1; while (1) { tmp = strstr(name+t+1, "\\"); // 从pathname解析出filename if (NULL == tmp) tmp = strstr(name+t+1, "/"); if (NULL != tmp) t = (int)(tmp-name); else break; } //动态内存分配 tmp = (char *)malloc(size * sizeof(char)); strcat(path, name+t+1); //上传文件内容类型 fprintf(cgiOut, "最终生成文件: "); cgiHtmlEscape(path); fprintf(cgiOut, "
\n"); //创建文件,以字节流的方式打开 fd = fopen(path, "wb+"); if (fd == NULL) { return ErrOpenFile; } // 从临时文件读出content while (cgiFormFileRead(file, tmp, size, &got) == cgiFormSuccess) { fwrite(tmp, size, sizeof(char), fd); //把读出的content写入新文件 } //打印输出 fprintf(cgiOut, "

上传文件成功.

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

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

交叉编译工具链按实际使用的来,下述只是示例。

方式一:
arm-linux-gcc -o upload.cgi upload.c -L ./-lcgic

方式二:
arm-linux-gcc -o upload.cgi upload.c cgic.c

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

你可能感兴趣的:(html,c语言)