背景:设备端(采集端) + 云端(服务端)+ 浏览端(客户端)这种结构是比较常见的,而在某些情形,云端如果涉及到跨境跨域的问题,上述的架构就不一定合适。在功能要求不是那么复杂的情况下,可以考虑设备端(采集端+服务端)+ 浏览端(客户端)这种架构。在设备端移植搭建服务器,并将显示的相关html文件部署其上,浏览器输入设备地址及html文件名,即可实现远程监控功能。
功能:点击网页上文件选择按钮,从本地选择待上传的文件,完成后点击上传按钮,将选中的文件上传到web server。
HTML内容如下
例子一:
文件上传
例子二(网友方案):
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错误,解决了后还是没有上传成功,有知晓的同学还望指点一二。