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

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

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

HTML内容如下

例子一:

<!DOCTYPE html>
<html>
<head>
    <title>文件上传</title>
</head>
<body>
    <form action="/cgi-bin/upload.cgi" enctype="multipart/form-data" method="post"> 
    <table>                             
	<tbody>
	<tr>
	    <td> upload file </td>
	    <td><input type="file" name="uploadfile" value=""></td>
	    <td><input type="submit" name="upload" value="上传"></td>
	</tr>
    </tbody>
    </table>
</form>
</body>
</html>

例子二(网友方案):

<!DOCTYPE html>
<html>
	<style>
		body
		{
			background-color: lightblue;
		}
		div
		{
			margin-left: 30px;
			margin-top: 30px;
		}
	</style>
    <head>
		<meta charset="utf-8">
        <title>Upload File Test</title>
    </head>
	
    <body>
        <form enctype='multipart/form-data' action="/cgi-bin/upload.cgi" method="post">
			<div>
				<span>上传路径:</span><input type="text" name="updatapath" value="/tmp/">
			</div>
            <div>
				<input type="file" name='updatafile' multiple>
			</div>
            <div>
				<input type="submit" value="确认上传">
			</div>
        </form >
    </body>
</html>

上传文件功能必须开启

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

CGI C实现:

例子一配套C代码:

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#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("<p>cgiFormNotFound. </p>\n");
			break;
		case cgiFormTruncated:
			printf("<p>cgiFormTruncated. </p>\n");
			break;
		case cgiFormSuccess:
			printf("<p>cgiFormSuccess. </p>\n");
			break;
		default:
			printf("<p>unknown. </p>\n");
			break;
	}
    if (ret != cgiFormSuccess) //获取客户端pathname 
    {
        printf("<p>%s No file was uploaded. </p>\n", name);
        return ErrNoFile;
    }
    fprintf(cgiOut, "The filename submitted was: ");
    cgiHtmlEscape(name);
    fprintf(cgiOut, "<br>\n");
	
    cgiFormFileSize("uploadfile", &size);
    fprintf(cgiOut, "The file size was: %d bytes<br>\n", size);
	
    cgiFormFileContentType("uploadfile", contentType, sizeof(contentType));
    fprintf(cgiOut, "The alleged content type of the file was: ");
    cgiHtmlEscape(contentType);
    fprintf(cgiOut, "<br>\n");
 
    if (cgiFormFileOpen("uploadfile", &file) != cgiFormSuccess)  //尝试打开上传的,并存放在系统中的临时文件
    {
        fprintf(cgiOut, "<p> Could not open the file. </p>\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, "<p> Upload File Success. </p>\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 <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <time.h>
#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,"<p> 文件上传失败. </p>\n");
           return ErrNoFile;
        }

        //显示上传文件内容
        fprintf(cgiOut, "提交上传文件名称: ");
        cgiHtmlEscape(name);//虽然已经获取到名称,如果文件名中有特殊的名称,将会被转换,总结:从html获取的字符串需要显示到网页的用这个比较好,用fprintf也可以。
        fprintf(cgiOut, "<br>\n");

        //获取文件大小
        cgiFormFileSize("updatafile", &size);
        fprintf(cgiOut, "文件大小为: %d 字节<br>\n", size);

        //上传文件内容类型
        cgiFormFileContentType("updatafile", contentType, sizeof(contentType));
        fprintf(cgiOut, "文件的内容类型为: ");
        cgiHtmlEscape(contentType);
        fprintf(cgiOut, "<br>\n");

        if (cgiFormString("updatapath", path, sizeof (path)) != cgiFormSuccess)
        {
            fprintf(cgiOut, "<p> Could not open the file. </p>\n");
            return ErrNonePath;
        }

        //上传文件内容类型
        fprintf(cgiOut, "文件的路径: ");
        cgiHtmlEscape(path);
        fprintf(cgiOut, "<br>\n");

        //尝试打开上传的,并存放在系统中的临时文件
        if (cgiFormFileOpen("updatafile", &file) != cgiFormSuccess)
        {
           fprintf(cgiOut, "<p> Could not open the file. </p>\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, "<br>\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, "<p> 上传文件成功. </p>\n");

        //关闭文件
        cgiFormFileClose(file);
        free(tmp);
        fclose(fd);

		//跳转回到主页面,这个需要浏览器html代码功能来实现
        //fprintf(cgiOut,"<meta http-equiv=\"Refresh\" content=\"3;URL=/index.html\">");
        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错误,解决了后还是没有上传成功,有知晓的同学还望指点一二。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值