Utilize CGI (C) and HTML to realize the upload function of PC local files

Background: The structure of device side (collection side) + cloud side (server side) + browser side (client) is relatively common. In some cases, if the cloud involves cross-border and cross-domain issues, the above architecture will be Not necessarily appropriate. When the functional requirements are not that complex, you can consider the architecture of the device side (collection side + server side) + browser side (client). Transplant and build a server on the device side, and deploy the displayed relevant html files on it. Enter the device address and html file name in the browser to realize the remote monitoring function.

Function: Click the file selection button on the web page to select the file to be uploaded locally. After completion, click the upload button to upload the selected file to the web server.

The HTML content is as follows

Example 1:

<!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>

Example 2 (netizen plan):

<!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>

The file upload function must be enabled

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

CGI C implementation:

Example 1 supporting C code:

#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();
	
}

Example 2 supporting C code (netizen’s solution, based on the cgitest.c example provided by CGI, which involves various features of 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;


}

        One thing to note when compiling the above CGI program is that cgiMain (and other CGI APIs) generally need to be linked to a static library (or directly bring cgic.c, of the same nature). For example, if the target file generated by the final compilation is upload.cgi, then you can Compile according to the following statement:

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

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

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

        In the early stage, I tried to implement the file upload function directly through html + js in order to save some space. Unfortunately, 4xx and 3xx errors occurred. After solving the problem, the upload was still not successful. Anyone who knows about it would like to give some advice.

Guess you like

Origin blog.csdn.net/DIANZI520SUA/article/details/133293716