1. 修改 package/busybox/busybox.config
关于http部分改成这样:
CONFIG_HTTPD=y
# CONFIG_FEATURE_HTTPD_RANGES is not set
# CONFIG_FEATURE_HTTPD_SETUID is not set
CONFIG_FEATURE_HTTPD_BASIC_AUTH=y
CONFIG_FEATURE_HTTPD_AUTH_MD5=y
CONFIG_FEATURE_HTTPD_CGI=y
CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR=y
CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV=y
CONFIG_FEATURE_HTTPD_ENCODE_URL_STR=y
CONFIG_FEATURE_HTTPD_ERROR_PAGES=y
2. 在 buildroot 目录下执行 make, output/build/busybox-1.27.2/.config 会被上面的 busybox.config 替换掉,
也会在 output/target/usr/bin 生成 http 软连接文件,指向的是 output/target/bin/busybox
3. 新建 output/target/etc/httpd2.conf 配置文件(也可以不要此文件),
参考: https://openwrt.org/docs/guide-user/services/webserver/http.httpd
4. 建立 output/target/init.d/S80httpd 文件:
/usr/sbin/httpd -p 80 -h /www -c /etc/httpd.conf
5. 新建 output/target/www/test.html 文件
<html>
<head>
<meta charset="utf-8">
<title> hello whycan.cn</title>
</head>
<body>
<b>I am come from whycan.cn. </b>
<p />
<font color="red">Run in 君正Ingenic X1000</font>
</body>
</html>
6. 新建 output/target/www/cgi-bin/ 文件夹
再建文件: output/target/www/cgi-bin/test 脚本文件:
#!/bin/sh
echo "Content-type: text/html"
echo ""
echo ""
echo "<html><head><meta charset=\"utf-8\"><title>Sample CGI Output @ whycan.cn</title></head><body>"
echo "<font color=\"red\">测试测试 红色</font>"
echo "<p />"
echo "<font color=\"blue\">测试测试 蓝色</font>"
echo "<p />"
echo "</body></html>"
7. 打包,烧录,运行.
最近编辑记录 歌以咏志 (2019-09-21 11:26:43)
离线
上图是test.html的显示页面
上图是test cgi脚本的显示页面
第一次学习html标签,做得丑,请大家多多包涵。
离线
这个选项可以在busybox的menuconfig里面开吧,手动改配置感觉有点不方便
离线
这个选项可以在busybox的menuconfig里面开吧,手动改配置感觉有点不方便
还真有这个命令: make busybox-menuconfig
用上面的命令就可以配置了。
离线
离线
发现用上面的比较麻烦, 找到 libfcgi 库, 在 buildroot 可以直接开启: BR2_PACKAGE_LIBFCGI=y
先进入目录 buildroot-2017.08.1/, 执行 make
再进入目录 buildroot-2017.08.1/output/build/libfcgi-2.4.0/examples, 执行 make
会生成好几个 cgi 程序, 拷贝到 rootfs 的 /www/cgi-bin/ 目录.
浏览器打开: http://192.168.1.33/cgi-bin/echo
一切 OK, 一个基本的 cgi 程序就跑起来了!!!
离线
也可以用curl 命令测试这个 cgi 服务器:
curl -F "image=@echo.c" http://192.168.1.33/cgi-bin/echo
curl -F "image=@echo.c" -d "eeeeeeeee=ffffffff" http://192.168.1.33/cgi-bin/echo?aaaa=bbb\&ccc=dddddddddddddd
curl -d "eeeeeeeee=ffffffff&gggggg=hhhhh" http://192.168.1.33/cgi-bin/echo
离线
#include <fcgi_stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
int main()
{
while(FCGI_Accept() >= 0)
{
char* strGetData = NULL;
int iContentLength = 0;
if (getenv("QUERY_STRING"))
{
strGetData = getenv("QUERY_STRING");
}
if (getenv("CONTENT_LENGTH"))
{
iContentLength = atoi(getenv("CONTENT_LENGTH"));
}
char* data = (char*)malloc(iContentLength + 1);
memset(data, 0, iContentLength + 1);
FCGI_fread(data, 1, iContentLength, FCGI_stdin);
FCGI_printf("Content-type:text/html\r\n\r\n");
FCGI_printf(data);
FCGI_printf("iContentLength = %d\n", iContentLength);
#if 0
FILE* fp = fopen("tmp.txt", "w");
fwrite(data, 1, iContentLength, fp);
fclose(fp);
#endif
}
return 0;
}
编译: /opt/buildroot-2017.08.1/output/host/bin/arm-linux-gcc -o cgi_test cgi_test.c -lfcgi
curl测试(上传一个echo.c文件): curl -F "image=@echo.c" http://192.168.1.33/cgi-bin/echo
离线
离线
感谢分享, 一个微型嵌入式 cgi web 服务器诞生了, 楼主能不能再研究一下,把 lua 整合进去, 用lua脚本写网页服务器。
搞定cgi之后考虑整合一个 lua 进去,毕竟脚本编程方便太多。
离线
豆瓣这个博客不错: https://www.douban.com/note/541320499/
如何用c实现fastcgi文件上传 steven 2016-02-24 18:18:37
在现在web端中,fastcgi算是比较古老的方式,很多公司依然在用fastcgi可能是出于历史架构的原因,Fastcgi确实在后端能和C完美地结合。
用c + fastcgi来处理文件上传比较繁琐,需要处理MIME, 文件内容boundary问题等一些问题。
所以要实现c + fastcgi 文件上传只需要掌握以下2点就可以了.
1. 从 stdin 读上传的文件内容.
2. 处理第1点中文件boundary问题.
第1点比较容易,fread直接搞定
第2点关于boundary问题,根据 rfc1341 (https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html)
描述,所有从web端POST过来的数据都是用 boudnary分隔的, 比如上传文件内容:
------WebKitFormBoundarycKuywINaoWuIidOR\r\n
Content-Disposition: form-data; name="file"; filename="test.png"\r\n
Content-Type: application/x-gzip\r\n
\r\n
\r\b
文件内容
------WebKitFormBoundarycKuywINaoWuIidOR\r\n
实现代码:
https://github.com/pengxiaojun/Fastcgi_utils/blob/master/upload.cpp
#include "upload.h"
#include "logger.h"
#include <fcgi_stdio.h>
#define DEAL_BUF_LEN 1024
#define SIGN_CODE_LEN 100
#define FILE_NAME_LEN 64
enum
{
STATE_START,
STATE_GET_SIGN_CODE,
STATE_GET_FILE_NAME,
STATE_GET_FILE_START,
STATE_GET_FILE_CONTENT,
STATE_CHECK_END,
STATE_END
};
int upload_file_save_as(const char *saveas)
{
FILE *fp;
int getState = STATE_START;
int contentLength; /* uploaded file content length */
int nowReadLen;
int signCodeLen;
int tmpLen;
char *nowReadP;
char *nowWriteP;
char dealBuf[DEAL_BUF_LEN];
char signCode[SIGN_CODE_LEN]; /* http boundary */
char tmpSignCode[SIGN_CODE_LEN];
char fileName[FILE_NAME_LEN];
memset(dealBuf, 0, DEAL_BUF_LEN);
memset(signCode, 0, SIGN_CODE_LEN);
memset(fileName, 0, FILE_NAME_LEN);
nowReadLen = 0;
if((char *)getenv("CONTENT_LENGTH")!=NULL)
{
contentLength = atoi((char *)getenv("CONTENT_LENGTH"));
}
else
{
return EUPLOAD_NO_DATA;
}
while(contentLength > 0)
{
if(contentLength >= DEAL_BUF_LEN)
{
nowReadLen = DEAL_BUF_LEN;
}
else
{
nowReadLen = contentLength;
}
contentLength -= nowReadLen;
if(fread(dealBuf, sizeof(char), nowReadLen, FCGI_stdin) != (size_t)nowReadLen)
{
log_error("read error %d", nowReadLen);
return EUPLOAD_READ;
}
nowReadP = dealBuf;
while(nowReadLen > 0)
{
switch (getState)
{
case STATE_START:
nowWriteP = signCode;
getState = STATE_GET_SIGN_CODE;
case STATE_GET_SIGN_CODE:
if(strncmp(nowReadP, "\r\n", 2) == 0)
{
signCodeLen = nowWriteP - signCode;
nowReadP++;
nowReadLen--;
*nowWriteP = 0;
getState = STATE_GET_FILE_NAME;
}
else
{
*nowWriteP = *nowReadP;
nowWriteP++;
}
break;
case STATE_GET_FILE_NAME:
if(strncmp(nowReadP,"filename=",strlen("filename=")) == 0)
{
nowReadP += strlen("filename=");
nowReadLen -= strlen("filename=");
nowWriteP = fileName + strlen(saveas);
while(*nowReadP != '\r')
{
if(*nowReadP == '\\' || *nowReadP == '/')
{
nowWriteP = fileName + strlen(saveas);
}
else if(*nowReadP != '\"')
{
*nowWriteP = *nowReadP;
nowWriteP++;
}
nowReadP++;
nowReadLen--;
}
*nowWriteP = 0;
nowReadP++;
nowReadLen--;
getState = STATE_GET_FILE_START;
memcpy(fileName, saveas, strlen(saveas));
if((fp = fopen(fileName, "w")) == NULL)
{
log_error("open file %s error %d", fileName, errno);
return EUPLOAD_WRITE;
}
}
break;
case STATE_GET_FILE_START:
if(strncmp(nowReadP, "\r\n\r\n", 4) == 0)
{
nowReadP += 3;
nowReadLen -= 3;
getState = STATE_GET_FILE_CONTENT;
}
break;
case STATE_GET_FILE_CONTENT:
if(*nowReadP != '\r')
{
fputc(*nowReadP,fp);
}
else
{
if(nowReadLen >= (signCodeLen + 2)) //\r\n=2
{
if(strncmp(nowReadP + 2, signCode, signCodeLen) == 0)
{
getState = STATE_END;
nowReadLen = 1;
}
else
{
fputc(*nowReadP,fp);
}
}
else
{
getState = STATE_CHECK_END;
nowWriteP = tmpSignCode;
*nowWriteP = *nowReadP;
nowWriteP++;
tmpLen = 1;
}
}
break;
case STATE_CHECK_END:
if(*nowReadP != '\r')
{
if(tmpLen < signCodeLen + 2)
{
*nowWriteP = *nowReadP;
nowWriteP++;
tmpLen++;
if(tmpLen == signCodeLen + 2)
{
*nowWriteP = 0;
if((tmpSignCode[1] == '\n')&&(strncmp(tmpSignCode + 2,signCode,signCodeLen) == 0))
{
getState = STATE_END;
nowReadLen = 1;
}
else
{
//fprintf(fp,tmpSignCode);
fwrite(tmpSignCode,sizeof(char),tmpLen,fp);
getState = STATE_GET_FILE_CONTENT;
}
}
}
}
else
{
*nowWriteP = 0;
//fprintf(fp,tmpSignCode);
fwrite(tmpSignCode,sizeof(char),tmpLen,fp);
nowWriteP = tmpSignCode;
*nowWriteP = *nowReadP;
nowWriteP++;
tmpLen = 1;
}
break;
case STATE_END:
nowReadLen = 1;
break;
default:break;
}
nowReadLen--;
nowReadP++;
}
}
if(fp != NULL)
{
fclose(fp);
}
return EUPLOAD_SUCCESS;
}
离线
用楼上@晕哥的代码保存文件OK了, 接下来要用 libcurl 做一个客户端.
代码在此: https://curl.haxx.se/libcurl/c/postit2.html
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.haxx.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
***************************************************************************/
/* <DESC>
* HTTP Multipart formpost with file upload and two additional parts.
* </DESC>
*/
/* Example code that uploads a file name 'foo' to a remote script that accepts
* "HTML form based" (as described in RFC1738) uploads using HTTP POST.
*
* The imaginary form we'll fill in looks like:
*
* <form method="post" enctype="multipart/form-data" action="examplepost.cgi">
* Enter file: <input type="file" name="sendfile" size="40">
* Enter file name: <input type="text" name="filename" size="30">
* <input type="submit" value="send" name="submit">
* </form>
*
* This exact source code has not been verified to work.
*/
#include <stdio.h>
#include <string.h>
#include <curl/curl.h>
int main(int argc, char *argv[])
{
CURL *curl;
CURLcode res;
curl_mime *form = NULL;
curl_mimepart *field = NULL;
struct curl_slist *headerlist = NULL;
static const char buf[] = "Expect:";
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if(curl) {
/* Create the form */
form = curl_mime_init(curl);
/* Fill in the file upload field */
field = curl_mime_addpart(form);
curl_mime_name(field, "sendfile");
curl_mime_filedata(field, "postit2.c");
/* Fill in the filename field */
field = curl_mime_addpart(form);
curl_mime_name(field, "filename");
curl_mime_data(field, "postit2.c", CURL_ZERO_TERMINATED);
/* Fill in the submit field too, even if this is rarely needed */
field = curl_mime_addpart(form);
curl_mime_name(field, "submit");
curl_mime_data(field, "send", CURL_ZERO_TERMINATED);
/* initialize custom header list (stating that Expect: 100-continue is not
wanted */
headerlist = curl_slist_append(headerlist, buf);
/* what URL that receives this POST */
curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/examplepost.cgi");
if((argc == 2) && (!strcmp(argv[1], "noexpectheader")))
/* only disable 100-continue header if explicitly requested */
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
curl_easy_setopt(curl, CURLOPT_MIMEPOST, form);
/* Perform the request, res will get the return code */
res = curl_easy_perform(curl);
/* Check for errors */
if(res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
/* always cleanup */
curl_easy_cleanup(curl);
/* then cleanup the form */
curl_mime_free(form);
/* free slist */
curl_slist_free_all(headerlist);
}
return 0;
}
安装软件包: sudo apt-get install libcurl4-openssl-dev -y
编译: gcc -o test postit2.c -lcurl -I/usr/include
然后执行: ./test
就可以通过 http 协议上传文件到 嵌入式设备了, 就像浏览器一样方便。
离线
文件服务器用go搭建不会超过10行代码,有时间在f1c100s试一下
离线
文件服务器用go搭建不会超过10行代码,有时间在f1c100s试一下
go 编译出来的体积有点大吧, 还有我这个是要用浏览器交互一些数据,不只是文件上传。
请教 go 如何实现一个文件服务器呢?愿闻其详。
离线
kekemuyu 说:文件服务器用go搭建不会超过10行代码,有时间在f1c100s试一下
go 编译出来的体积有点大吧, 还有我这个是要用浏览器交互一些数据,不只是文件上传。
请教 go 如何实现一个文件服务器呢?愿闻其详。
以下是实现文件浏览和下载功能的代码,上传的话可能需更多代码,需要做个页面和添加相应服务。总之。只要是涉及到网络的需求,go都能非常优雅的完成。
package main
import (
"fmt"
"net/http"
)
func main() {
mux := http.NewServeMux()
mux.Handle("/files/", http.StripPrefix("/files/", http.FileServer(http.Dir("../files"))))
if err := http.ListenAndServe(":3000", mux); err != nil {
log.Fatal(err)
}
}
最近编辑记录 kekemuyu (2019-10-28 08:49:25)
离线
go的库真全。
离线
busybox 配置还可以这么改:
make busybox-menuconfig
或者直接修改这个文件: output/build/busybox-1.31.0/.config
离线