最近有个项目要求做到通过web方式在线更新一款嵌入式(snmp agent)设备的应用程序。所以做了以下调试,然后权衡利弊后选择第三种方式
这里介绍三种方式实现web上传文件:
*1. 使用插件*
下面这个连接介绍了十种插件。但是由于官方下载的插件例子都是php作为后台处理脚本,我做的嵌入式应用是不支持PHP的,只能支持C和shell。
http://www.chinaz.com/free/2013/0409/298937.shtml
upload.html
<html>
<head>
<title>My Uploadify Implementation</title>
<link rel="stylesheet" type="text/css" href="commJS/uploadify/uploadify.css" />
<script type="text/javascript" src="commJS/jquery-1.8.2.min.js"></script>
<script type="text/javascript" src="commJS/uploadify/jquery.uploadify.js"></script>
<script type="text/javascript" src="commJS/uploadify/jquery.uploadify.min.js"></script>
<script type="text/javascript">
$(function() {
$('#file_upload').uploadify({
'swf' : 'commJS/uploadify/uploadify.swf',
'uploader' : 'commJS/uploadify/uploadify.php',
'script': 'commJS/uploadify/uploadify.sh',
'fileTypeDesc':'Image Files',
'fileTypeExts':'*.jpg;*.jpeg;*.gif;*.png;*.bmp',
'buttonText' : 'BROWSE...',
'debug' : true,
'auto' : true, //是否自动上传,
'cancelImage' : 'commJS/uploadify/uploadify-cancel.png',
//修改formData数据
'onUploadStart' : function(file) {
$("#file_upload").uploadify("settings", "someOtherKey", 2);
},
//上传成功
'onUploadSuccess' : function(file, data,
response) {
alert(file.name + ' | ' + response + ':' + data);
},
'onUploadComplete' : function(file) {
alert('The file ' + file.name + ' finished processing.');
}
// Your options here
});
});
</script>
</head>
<body>
<input type="file" name="file_upload" id="file_upload" />
</body>
</html>
PHP:
<?php
/*
Uploadify
Copyright (c) 2012 Reactive Apps, Ronnie Garcia
Released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
*/
// Define a destination
$targetFolder = '/uploads'; // Relative to the root
$verifyToken = md5('unique_salt' . $_POST['timestamp']);
if (!empty($_FILES) && $_POST['token'] == $verifyToken) {
$tempFile = $_FILES['Filedata']['tmp_name'];
$targetPath = $_SERVER['DOCUMENT_ROOT'] . $targetFolder;
$targetFile = rtrim($targetPath,'/') . '/' . $_FILES['Filedata']['name'];
// Validate the file type
$fileTypes = array('jpg','jpeg','gif','png'); // File extensions
$fileParts = pathinfo($_FILES['Filedata']['name']);
if (in_array($fileParts['extension'],$fileTypes)) {
move_uploaded_file($tempFile,$targetFile);
echo '1';
} else {
echo 'Invalid file type.';
}
}
?>
*2. 使用Post方式*
经过测试发现这种方式一次只能传送3KB大小的文件,而我要上传的文件有5M左右,所以此方式感觉行不通,或者可以修改代码实现分段上传;但是感觉实现起来太麻烦就放弃了。
参考:代码做了一下修改以实现文件成功上传。
http://www.justwinit.cn/post/6314/
http://wenku.baidu.com/link?url=XDk_VErYp8H-y4Rjw8a5g832FDEK6U0N-P_PrMSn_BCdCKTuHszrTM06xVOuos41olCNAyIXLMC_0lJrSDH6_uQgzeTRmS8CxUS1X-Xesju
upload.html
<FORM METHOD="POST" id="form1" name="form1" ENCTYPE="multipart/form-data" ACTION="cgi-bin/setUpgrade.cgi">
select file:<INPUT TYPE="FILE" NAME="FILE1" id="FILE1"><INPUT TYPE="button" onclick="upload_file()" VALUE="上传" ><br>
<center>
<div id="uploadInfo"></div>
</center>
</FORM>
setUpgrade.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <volcfg.h>
#include <shmram.h>
#include <volmibvar.h>
#include <s_share.h>
#include <cgi.h>
#include <gsemaphore.h>
#include <volSNTPTask.h>
#define UPLOAD_PATH "/root/upgrade.tar.gz" //"../htdocs/ac483/ac483_bak"
void fit(char *,unsigned size);
int main(int argc, char *argv[])
{
printf("Content-Type:text/html\n\n");
char *pMethod = getenv("REQUEST_METHOD");
//printf("pMethod=%s\n",pMethod);
if(pMethod == NULL || *pMethod == 0)
{
printf("No Any Method!\n");
return 0;
}
if(strcmp(pMethod, "GET") == 0)
return 1;
if(strcmp(pMethod, "POST") == 0)
{
char *pCntLen = getenv("CONTENT_LENGTH");
//printf("pCntLen=%s\n",pCntLen);
if(!pCntLen)
{
printf("Can't get Content_Length!\n");
return 0;
}
if(*pCntLen == 0)
{
printf("Can't get Content_Length!\n");
return 0;
}
int StrLen = atoi(pCntLen);
// printf("StrLen=%d\n",StrLen);
if(StrLen <= 0)
{
printf("String Length <= 0\n");
return 0;
}
//char *ph = getenv("CONTENT_TYPE");
//printf("ph=%s\n",ph);
char *readstr=(char *)malloc(StrLen+1);
fread(readstr,StrLen,1,stdin);
// printf("readstr=%s\n",readstr);
int n=0;
int firstLineMark=0;
int firstLineCount=0;
int headCount=0;
while(n<4)
{
if(*(readstr++)=='\n')
{
firstLineMark++;
n++;
}
if(firstLineMark==0)
{
firstLineCount++;
}
headCount++;
}
StrLen = StrLen-headCount;
fit(readstr,13);
/*if(strcmp(readstr,ACHEAD)!=0)
{
printf("<SCRIPT language=JavaScript>alert('file format not support!');javascript:history.go(-1)</SCRIPT>");
return 0;
}
*/
FILE *fp;
if((fp=fopen(UPLOAD_PATH,"wb"))==NULL)
{
printf("error open file\n");
return 0;
}
fwrite(readstr+13,StrLen-13-firstLineCount-5,1,fp);
fclose(fp);
printf("<SCRIPT language=JavaScript>alert('congratulations!');javascript:history.go(-1)</SCRIPT>");
}
return 0;
}
void fit(char *string,unsigned size)
{
if(strlen(string)>size)
*(string+size)='\0';
}
*3. 使用websocket方式上传文件*
文件上传过程:先建立sock连接,web client通过sock发送要上传的文件名字,收到服务器返回的ACK后就开始发送文件,每次发送1KB并等待服务器返回ACK后发送下1KB直到全部发送完成,最后发送Send success。
web界面参考了下面的连接
http://www.cnblogs.com/tianma3798/p/5852527.html
服务器端则参考了下面的连接:对于服务器程序有两个地方我是建议修改的,一个是每次调用analyData();函数的时候要先释放掉前一次占用的内存空间(加入代码 if(payloadData !=NULL) free(payloadData); );二是response(); 中的
if(!data ) //这里还是个野指针。
{
return;
}
改为
if(!message) // data 改为 message
{
return;
}
http://blog.csdn.net/xxdddail/article/details/19070149
**
Code:
**
upload.html
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
</head>
<body>
<div >
<div >
<div >Block read the file:</div>
<div id="bodyOne">
<input type="file" value="Browse..." id="file" multiple/>
<input type="button" onclick="upgrade()" value="Upgrade"/><br />
</div>
</div>
</div>
</body>
<script type="text/javascript">
//封装 单个文件上传实例
(function () {
var ip_addr = document.location.hostname;
var url = 'ws://'+ip_addr+":8000";
//指定上传文件,创建上传操作对象
function uploadOperate(file) {
var _this = this;
this.reader = new FileReader();//读取文件对象
this.step = 1024 * 1;//每次读取文件字节数
this.curLoaded = 0; //当前读取位置
this.file = file; //当前文件对象
this.enableRead = true; //指定是否可读取,
this.total = file.size; //当前文件总大小
this.startTime = new Date(); //开始读取时间
//创建显示
this.createItem();
this.initWebSocket(function () {
_this.bindReader();
});
console.info('file size:' + this.total);
}
uploadOperate.prototype = {
//绑定读取事件
bindReader: function () {
var _this = this;
var reader = this.reader;
var ws = this.ws;
if (_this.curLoaded <= 0)
{
ws.send(_this.file.name);
console.log('----ws.send(_this.file.name);...');
}
reader.onload = function (e) {
//判断是否能再次读取
if (_this.enableRead == false) return;
//根据当前缓冲区 控制读取速度
if (ws.bufferedAmount >= _this.step * 20) {
setTimeout(function () {
_this.loadSuccess(e.loaded);
}, 5);
console.info('----wait...');
} else {
_this.loadSuccess(e.loaded);
}
}
//开始读取
//_this.readBlob();
},
//读取成功,操作处理
loadSuccess: function (loaded) {
var _this = this;
var ws = _this.ws;
//使用WebSocket 将二进制输出上传到服务器
var blob = _this.reader.result;
ws.send(blob);//send to server
console.log('----ws.send(blob);...');
//当前发送完成,继续读取
_this.curLoaded += loaded;
if (_this.curLoaded < _this.total) {
} else {
//发送读取完成
ws.send('Send success.');
console.log('----ws.send(\'Send success.\');...');
//读取完成
console.log('Total upload:' + _this.curLoaded + ',Total time:' + (new Date().getTime() - _this.startTime.getTime()) / 1000);
}
//显示进度等
_this.showProgress();
},
//创建显示项
createItem: function () {
var _this = this;
var blockquote = document.createElement('blockquote');
var abort = document.createElement('input');
abort.type = 'button';
abort.value = 'Pause';
abort.onclick = function () {
_this.stop();
};
blockquote.appendChild(abort);
var containue = document.createElement('input');
containue.type = 'button';
containue.value = 'Continue';
containue.onclick = function () {
_this.containue();
};
blockquote.appendChild(containue);
var progress = document.createElement('progress');
progress.style.width = '400px';
progress.max = 100;
progress.value = 0;
blockquote.appendChild(progress);
_this.progressBox = progress;
var status = document.createElement('p');
status.id = 'Status';
blockquote.appendChild(status);
_this.statusBox = status;
document.getElementById('bodyOne').appendChild(blockquote);
},
//显示进度
showProgress: function () {
var _this = this;
var percent = (_this.curLoaded / _this.total) * 100;
_this.progressBox.value = percent;
_this.statusBox.innerHTML = percent;
},
//执行读取文件
readBlob: function () {
var blob = this.file.slice(this.curLoaded, this.curLoaded + this.step);
this.reader.readAsArrayBuffer(blob);
},
//中止读取
stop: function () {
this.enableRead = false;
this.reader.abort();
console.log('Stop read,curLoaded:' + this.curLoaded);
},
//继续读取
containue: function () {
this.enableRead = true;
this.readBlob();
console.log('Continue read,curLoaded:' + this.curLoaded);
},
//初始化 绑定创建连接
initWebSocket: function (onSuccess) {
var _this = this;
var ws = this.ws = new WebSocket(url); //初始化上传对象
ws.onopen = function () {// 三次握手---horsen
console.log('connect creat success');
if (onSuccess)
onSuccess();
}
ws.onmessage = function (e) {
var data = e.data;
if (isNaN(data) == false) {
console.log('Sever receive success:' + data);
} else {
console.log(data);
_this.readBlob();-----------------------------收到服务器返回的ok,才发送下一个数据
}
}
ws.onclose = function (e) {
//中止读取
_this.stop();
console.log('connect is broken');
}
ws.onerror = function (e) {
//中止读取
_this.stop();
console.log('Exception occurs:' + e.message);
}
}
};
window.uploadOperate = uploadOperate;
})();
</script>
<script type="text/javascript">
//var fileBox = document.getElementById('file');
// fileBox.onChange = function () {
function upgrade() {
var fileBox = document.getElementById('file');
var files = fileBox.files;
for (var i = 0; i < files.length; i++) {
var file = files[i];
var operate = new uploadOperate(file);
}
}
</script>
</html>
uploadserver.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <volcfg.h>
#include <shmram.h>
#include <volmibvar.h>
#include <s_share.h>
#include <cgi.h>
#include <gsemaphore.h>
#include <volSNTPTask.h>
#include "base64.h"
#include "sha1.h"
#include "intLib.h"
#define UPLOAD_PATH "/root/"
#define REQUEST_LEN_MAX 1024*10
#define DEFEULT_SERVER_PORT 8000
#define WEB_SOCKET_KEY_LEN_MAX 256
#define RESPONSE_HEADER_LEN_MAX 1024
#define LINE_MAX 256
#if(1)
void shakeHand(int connfd,const char *serverKey);
char * fetchSecKey(const char * buf);
char * computeAcceptKey(const char * buf);
char * analyData(const char * buf,const int bufLen, int* datalen);
char * packData(const char * message,unsigned long * len);
void response(const int connfd,const char * message);
int saveFile(FILE **fp,char* buf,int length);
#endif
int main(int argc, char *argv[])
{
struct sockaddr_in servaddr, cliaddr;
socklen_t cliaddr_len;
int listenfd, connfd;
char buf[REQUEST_LEN_MAX];
char tempbuf[32]={"\0"};
char *data;
char str[INET_ADDRSTRLEN];
char *secWebSocketKey;
int i,n,datalength,datalengthAll=0;
int connected=0;//0:not connect.1:connected.
int port= DEFEULT_SERVER_PORT;
FILE *fp;
if(argc>1)
{
port=atoi(argv[1]);
}
if(port<=0||port>0xFFFF)
{
printf("Port(%d) is out of range(1-%d)\n",port,0xFFFF);
return;
}
listenfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(port);
bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
listen(listenfd, 20);
printf("Listen %d\nAccepting connections ...\n",port);
cliaddr_len = sizeof(cliaddr);
connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);
printf("From %s at PORT %d\n",
inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
ntohs(cliaddr.sin_port));
while (1)
{
memset(buf,0,REQUEST_LEN_MAX);
//printf("--------read..-------------\n");
n = read(connfd, buf, REQUEST_LEN_MAX);
if(0==n) break;
if(0==connected)
{
//printf("read:%d\n%s\n",n,buf);
secWebSocketKey=computeAcceptKey(buf);
shakeHand(connfd,secWebSocketKey);
connected=1;
continue;
}
//printf("******************data analy*****************\n");
data=analyData(buf,n,&datalength);
fflush(stdout);
datalengthAll += datalength;
//printf("reveice size:%d\r",datalengthAll);
if(memcmp(data,"Send success.",13)==0) {
printf("Receive success!\n");
sprintf(tempbuf,"%d",datalengthAll);
response(connfd,tempbuf);
sleep(2);
break;
}
saveFile(&fp,data,datalength);
response(connfd,"ACK\n");
}
fclose(fp);
close(connfd);
}
#if(1)
int saveFile(FILE **fp,char* buf,int length){
int res=0;
if(*fp==NULL){
char sfile[64]={"\0"};
sprintf(sfile,"%s%s",UPLOAD_PATH,buf);
if(access(sfile,R_OK) == 0){// if file have exist ,remove it.
if(unlink(sfile)<0){
printf("unlink %s error !\n",sfile);
return 0;
}
}
printf("sfile:%s--buflen(%d)!\n",sfile,strlen(buf));
if((*fp=fopen(sfile,"wb"))==NULL)// creat a file.
{
printf("error open file %s\n",sfile);
return 0;
}
printf("saveFile creat file %s success! %d !\n",sfile,length);
return 1;
}
res= fwrite(buf,length,1,*fp);
//printf("res=%d---length=%d--\n",res,length);//debug-----------------
return length;
}
/*******************************************************/
char * fetchSecKey(const char * buf)
{
char *key;
char *keyBegin;
char *flag="Sec-WebSocket-Key: ";
int i=0, bufLen=0;
key=(char *)malloc(WEB_SOCKET_KEY_LEN_MAX);
memset(key,0, WEB_SOCKET_KEY_LEN_MAX);
if(!buf)
{
return NULL;
}
keyBegin=strstr(buf,flag);
if(!keyBegin)
{
return NULL;
}
keyBegin+=strlen(flag);
bufLen=strlen(buf);
for(i=0;i<bufLen;i++)
{
if(keyBegin[i]==0x0A||keyBegin[i]==0x0D)
{
break;
}
key[i]=keyBegin[i];
}
return key;
}
char * computeAcceptKey(const char * buf)
{
char * clientKey;
char * serverKey;
char * sha1DataTemp;
char * sha1Data;
short temp;
int i,n;
const char * GUID="258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
if(!buf)
{
return NULL;
}
clientKey=(char *)malloc(LINE_MAX);
memset(clientKey,0,LINE_MAX);
clientKey=fetchSecKey(buf);
if(!clientKey)
{
return NULL;
}
strcat(clientKey,GUID);
sha1DataTemp=sha1_hash(clientKey);
n=strlen(sha1DataTemp);
sha1Data=(char *)malloc(n/2+1);
memset(sha1Data,0,n/2+1);
for(i=0;i<n;i+=2)
{
sha1Data[i/2]=htoi(sha1DataTemp,i,2);
}
serverKey = base64_encode(sha1Data, strlen(sha1Data));
return serverKey;
}
void shakeHand(int connfd,const char *serverKey)
{
char responseHeader [RESPONSE_HEADER_LEN_MAX];
if(!connfd)
{
return;
}
if(!serverKey)
{
return;
}
memset(responseHeader,'\0',RESPONSE_HEADER_LEN_MAX);
sprintf(responseHeader, "HTTP/1.1 101 Switching Protocols\r\n");
sprintf(responseHeader, "%sUpgrade: websocket\r\n", responseHeader);
sprintf(responseHeader, "%sConnection: Upgrade\r\n", responseHeader);
sprintf(responseHeader, "%sSec-WebSocket-Accept: %s\r\n\r\n", responseHeader, serverKey);
printf("Response Header:%s\n",responseHeader);
write(connfd,responseHeader,strlen(responseHeader));
}
/***********************************************************************/
char * analyData(const char * buf,const int bufLen, int* datalen)
{
char * data;
char fin, maskFlag,masks[4];
static char * payloadData=NULL;
char temp[8];
unsigned long n, payloadLen=0;
unsigned short usLen=0;
int i=0;
if(payloadData !=NULL) free(payloadData);// 释放上次读取数据的内存空间,数据已经保存到文件里面去了
if (bufLen < 2)
{
return NULL;
}
fin = (buf[0] & 0x80) == 0x80; // 1bit,1表示最后一帧
if (!fin)
{
return NULL;// 超过一帧暂不处理
}
maskFlag = (buf[1] & 0x80) == 0x80; // 是否包含掩码
if (!maskFlag)
{
return NULL;// 不包含掩码的暂不处理
}
payloadLen = buf[1] & 0x7F; // 数据长度
if (payloadLen == 126)
{
memcpy(masks,buf+4, 4);
payloadLen =(buf[2]&0xFF) << 8 | (buf[3]&0xFF);
payloadData=(char *)malloc(payloadLen);
memset(payloadData,0,payloadLen);
memcpy(payloadData,buf+8,payloadLen);
}
else if (payloadLen == 127)
{
memcpy(masks,buf+10,4);
for ( i = 0; i < 8; i++)
{
temp[i] = buf[9 - i];
}
memcpy(&n,temp,8);
payloadData=(char *)malloc(n);
memset(payloadData,0,n);
memcpy(payloadData,buf+14,n);//toggle error(core dumped) if data is too long.
payloadLen=n;
}
else
{
memcpy(masks,buf+2,4);
payloadData=(char *)malloc(payloadLen);
memset(payloadData,0,payloadLen);
memcpy(payloadData,buf+6,payloadLen);
}
for (i = 0; i < payloadLen; i++)
{
payloadData[i] = (char)(payloadData[i] ^ masks[i % 4]);
}
//printf("data(%d):%s\n",(int)payloadLen,payloadData);
*datalen = (int)payloadLen;
return payloadData;
}
char * packData(const char * message,unsigned long * len)
{
char * data=NULL;
unsigned long n;
n=strlen(message);
if (n < 126)
{
data=(char *)malloc(n+2);
memset(data,0,n+2);
data[0] = 0x81;
data[1] = n;
memcpy(data+2,message,n);
*len=n+2;
}
else if (n < 0xFFFF)
{
data=(char *)malloc(n+4);
memset(data,0,n+4);
data[0] = 0x81;
data[1] = 126;
data[2] = (n>>8 & 0xFF);
data[3] = (n & 0xFF);
memcpy(data+4,message,n);
*len=n+4;
}
else
{
// 暂不处理超长内容
*len=0;
}
return data;
}
void response(int connfd,const char * message)
{
char * data;
unsigned long n=0;
int i;
if(!connfd)
{
return;
}
if(!message) // data 改为 message
{
return;
}
data=packData(message,&n);
if(!data||n<=0)
{
printf("data is empty!\n");
return;
}
write(connfd,data,n);
}
#endif