基于C语言实现HTTP POST 表单形式上传文件

     最近在C语言项目开发中,需要用到了http上传文件,又不想用curl库,于是打算基于socket用C语言实现,也正好研究一波表单上传形式。 

1、首先写一个上传文件html文件: file.html

<!DOCTYPE>
<html>
  <head>
    <meta charset="utf-8">
    <title>File upload</title>
  </head>
  <body>
    <div>
      <form role="form" action="http://test.inteink.com/Server/interface_picture_upload.action?appid=wmj_BZ2WeU7K1iP&appsecret=MXkMcT43JsIgAwBwVJ30EbA2ovIcpfCQ&sn=T1868881608",
            method="POST" enctype="multipart/form-data">
			
            
            <input type="file" name="file" multiple>
            <button type="submit">Upload</button>
      </form>
    </div>
  </body>
</html>

  2、用火狐浏览器打开这个文件,选择一张图片点击传,并抓包,  如下所示。

OK,把头信息照抄一遍,头里面有些信息可以不要,也能上传成功,自己慢慢去尝试。

然后,再看一下参数项的请求负载。

简单分析下,发现请求负载中出现了几次 -----------------------------5907704431277628128378170588--,查询相关资料得知这是表单分隔符,可以自己定义。然后发现中间出现了一大串乱码,毫无疑问,这一定就是我们上传的文件内容了。按照这个思路,再计算一下Content-Length 的大小,定义一个足够大得buffer,将文件数据按这种形式放入buffer,写入套接字中即可。

因为http一般都会有回复信息,所以将数据写入完成后,立即读取套接字,将回复的信息保存至buffer中进行分析。

 

如下为自己写的代码以供大家参考:

http_post.c

#include <stdlib.h>  
#include <sys/types.h>  
#include <stdio.h>  
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <string.h>  
#include <sys/stat.h>  
#include "post.h"


unsigned char header[1024]={0}; 
unsigned char send_request[1024]={0};
unsigned char send_end[1024]={0};
unsigned char http_boundary[64]={0};
  
unsigned long get_file_size(const char *path)  //获取文件大小
{    
    unsigned long filesize = -1;        
    struct stat statbuff;    
    if(stat(path, &statbuff) < 0){    
        return filesize;    
    }else{    
        filesize = statbuff.st_size;    
    }    
    return filesize;    
}   

int http_post_upload_pic(const unsigned char *IP, const unsigned int port,char *URL, const char *filepath,
									char *ack_json, int ack_len) //Post方式上传图片
{
	int cfd = -1;
    int recbytes = -1;
    int sin_size = -1;
	char buffer[1024*10]={0};     
    struct sockaddr_in s_add,c_add;
	
	cfd = socket(AF_INET, SOCK_STREAM, 0);  //创建socket套接字
    if(-1 == cfd)  
    {  
        printf("socket fail ! \r\n");  
        return -1;  
    }
	
	bzero(&s_add,sizeof(struct sockaddr_in));  
    s_add.sin_family=AF_INET; //IPV4
    s_add.sin_addr.s_addr= inet_addr(IP);  
    s_add.sin_port=htons(port);
    printf("s_addr = %#x ,port : %#x\r\n",s_add.sin_addr.s_addr,s_add.sin_port);

	if(-1 == connect(cfd,(struct sockaddr *)(&s_add), sizeof(struct sockaddr)))  //建立TCP连接
    {  
        printf("connect fail !\r\n");  
        return -1;  
    }

	//获取毫秒级的时间戳用于boundary的值
	long long int timestamp;
	struct timeval tv;
	gettimeofday(&tv,NULL);
	timestamp = (long long int)tv.tv_sec * 1000 + tv.tv_usec;
	snprintf(http_boundary,64,"---------------------------%lld",timestamp);
	
	unsigned long totalsize = 0;
	unsigned long filesize = get_file_size(filepath); //文件大小
	unsigned long request_len = snprintf(send_request,1024,UPLOAD_REQUEST,http_boundary,filepath); //请求信息
	unsigned long end_len = snprintf(send_end,1024,"\r\n--%s--\r\n",http_boundary); //结束信息
	totalsize = filesize + request_len + end_len;

	unsigned long head_len = snprintf(header,1024,HTTP_HEAD,SERVER_PATH,URL,http_boundary,totalsize); //头信息
	totalsize += head_len;

    char* request = (char*)malloc(totalsize);	//申请内存用于存放要发送的数据
    if (request == NULL){  
        printf("malloc request fail !\r\n");  
        return -1;  
    }  
    request[0] = '\0';

	/******* 拼接http字节流信息 *********/	
    strcat(request,header);  									//http头信息
    strcat(request,send_request);    							//文件图片请求信息
	FILE* fp = fopen(filepath, "rb+");							//打开要上传的图片
    if (fp == NULL){  
        printf("open file fail!\r\n");  
        return -1;  
    }
	int readbyte = fread(request+head_len+request_len, 1, filesize, fp);//读取上传的图片信息
    if(readbyte < 1024) //小于1024个字节 则认为图片有问题
	{
		printf("Read picture data fail!\r\n");  
        return -1;
	}
    memcpy(request+head_len+request_len+filesize,send_end,end_len);  //http结束信息

	/*********  发送http 请求 ***********/
	if(-1 == write(cfd,request,totalsize)) 					
    {  
        printf("send http package fail!\r\n");  
        return -1;  
    }

	/*********  接受http post 回复的json信息 ***********/
	if(-1 == (recbytes = read(cfd,buffer,10240)))  
    {  
        printf("read http ACK fail !\r\n");  
        return -1;  
    }  
   	buffer[recbytes]='\0';
	int index = 0,start_flag = 0;
	int ack_json_len = 0;
	for(index = 0; index<recbytes; index++)
	{
		if(buffer[index] == '{')
		{
			start_flag = 1;
		}
		if(start_flag)
			ack_json[ack_json_len++] = buffer[index];  //遇到左大括号则开始拷贝

		if(buffer[index] == '}')		//遇到右大括号则停止拷贝
		{
			ack_json[ack_json_len] = '\0';
			break;
		}
	}
	if(ack_json_len > 0 && ack_json[ack_json_len-1] == '}')  //遇到花括号且有json字符串
	{
		printf("Receive:%s\n",ack_json);
	}
	else
	{
		ack_json[0] = '\0';
		printf("Receive http ACK fail!!\n");
		printf("--- ack_json_len = %d\n",ack_json_len);
	}
	
    free(request);  
    fclose(fp);  
    close(cfd);
	return 0;
}

int main(int argc, char *argv[])  
{  
	int ack_len = 256;
	char ack_json[256]={0};
	int ret = http_post_upload_pic(SERVER_ADDR, SERVER_PORT,SERVER_URL,argv[1],ack_json,ack_len); //Post方式上传图片
	if(ret == -1)
	{
		printf("\n\n----------- Post picture Fail!!\n");
	}
	return 0;  
}

http_post.h

#ifndef __HTTP_POST__
#define __HTTP_POST__

#define SERVER_ADDR	"47.97.190.1"
#define SERVER_PORT	80
#define SERVER_URL	"test.inteink.com"
#define SERVER_PATH	"/Server/interface_picture_upload.action?appid=wmj_BZ2WeU7K1iP&appsecret=MXkMcT43JsIgAwBwVJ30EbA2ovIcpfCQ&sn=T1868881608"

#define HTTP_HEAD 	"POST %s HTTP/1.1\r\n"\
					"Host: %s\r\n"\
					"User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:59.0) Gecko/20100101 Firefox/59.0\r\n"\
					"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"\
					"Accept-Language: en-US,en;q=0.5\r\n"\
					"Accept-Encoding: gzip, deflate\r\n"\
					"Content-Type: multipart/form-data; boundary=%s\r\n"\
					"Content-Length: %ld\r\n"\
					"Connection: close\r\n"\
					"Upgrade-Insecure-Requests: 1\r\n"\
					"DNT: 1\r\n\r\n"\
					
					
#define UPLOAD_REQUEST	"--%s\r\n"\
						"Content-Disposition: form-data; name=\"file\"; filename=\"%s\"\r\n"\
						"Content-Type: image/jpeg\r\n\r\n"
						
unsigned long get_file_size(const char *path);

int http_post_upload_pic(const unsigned char *IP, const unsigned int port,char *URL, const char *filepath,
									char *ack_json, int ack_len); //Post方式上传图片

#endif

 

  • 13
    点赞
  • 78
    收藏
    觉得还不错? 一键收藏
  • 20
    评论
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值