libcurl上傳圖片

快速產生libcurl程式碼

首先先用curl指令發送request,確認可以發送成功後可以用指令自動產生程式碼,在這裡我們將上傳一張圖片到http server,利用curl讀取電腦的圖片並且上傳

1
curl -X POST "http://localhost:8000/upload" -H "accept: application/json" -H "Content-Type: multipart/form-data" -F "uploadedFile=@a.jpg;type=image/jpeg" -F "EventTime=2022-01-01"

利用curl的--libcurl選項就可以快速產生c語言的程式碼,程式碼會被儲存到code.c

1
curl -X POST "http://localhost:8000/upload" -H "accept: application/json" -H "Content-Type: multipart/form-data" -F "uploadedFile=@a.jpg;type=image/jpeg" -F "EventTime=2022-01-01" --libcurl code.c

產生的程式碼如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/********* Sample code generated by the curl command line tool **********
* All curl_easy_setopt() options are documented at:
* https://curl.haxx.se/libcurl/c/curl_easy_setopt.html
************************************************************************/
#include <curl/curl.h>

int main(int argc, char *argv[])
{
CURLcode ret;
CURL *hnd;
curl_mime *mime1;
curl_mimepart *part1;
struct curl_slist *slist1;

mime1 = NULL;
slist1 = NULL;
slist1 = curl_slist_append(slist1, "accept: application/json");
slist1 = curl_slist_append(slist1, "Content-Type: multipart/form-data");

hnd = curl_easy_init();
curl_easy_setopt(hnd, CURLOPT_BUFFERSIZE, 102400L);
curl_easy_setopt(hnd, CURLOPT_URL, "http://localhost:8000/upload");
curl_easy_setopt(hnd, CURLOPT_NOPROGRESS, 1L);
mime1 = curl_mime_init(hnd);
part1 = curl_mime_addpart(mime1);
curl_mime_filedata(part1, "a.jpg");
curl_mime_name(part1, "uploadedFile");
curl_mime_type(part1, "image/jpeg");
part1 = curl_mime_addpart(mime1);
curl_mime_data(part1, "2022-01-01", CURL_ZERO_TERMINATED);
curl_mime_name(part1, "EventTime");
curl_easy_setopt(hnd, CURLOPT_MIMEPOST, mime1);
curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, slist1);
curl_easy_setopt(hnd, CURLOPT_USERAGENT, "curl/7.68.0");
curl_easy_setopt(hnd, CURLOPT_MAXREDIRS, 50L);
curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, (long)CURL_HTTP_VERSION_2TLS);
curl_easy_setopt(hnd, CURLOPT_SSH_KNOWNHOSTS, "/home/ai_server/.ssh/known_hosts");
curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(hnd, CURLOPT_FTP_SKIP_PASV_IP, 1L);
curl_easy_setopt(hnd, CURLOPT_TCP_KEEPALIVE, 1L);

/* Here is a list of options the curl code used that cannot get generated
as source easily. You may select to either not use them or implement
them yourself.

CURLOPT_WRITEDATA set to a objectpointer
CURLOPT_INTERLEAVEDATA set to a objectpointer
CURLOPT_WRITEFUNCTION set to a functionpointer
CURLOPT_READDATA set to a objectpointer
CURLOPT_READFUNCTION set to a functionpointer
CURLOPT_SEEKDATA set to a objectpointer
CURLOPT_SEEKFUNCTION set to a functionpointer
CURLOPT_ERRORBUFFER set to a objectpointer
CURLOPT_STDERR set to a objectpointer
CURLOPT_HEADERFUNCTION set to a functionpointer
CURLOPT_HEADERDATA set to a objectpointer

*/

ret = curl_easy_perform(hnd);

curl_easy_cleanup(hnd);
hnd = NULL;
curl_mime_free(mime1);
mime1 = NULL;
curl_slist_free_all(slist1);
slist1 = NULL;

return (int)ret;
}
/**** End of sample code ****/

curl_mime_filedata改成curl_mime_data

由於我的目的是將在記憶體中已經被encode好的圖片直接上傳,所以要用curl_mime_data
直接上傳記憶體內的內容。
注意除了用curl_mime_data放入資料以外,還必須自己手動把Content-Disposition補上filename。
另外記得curl_mime_data不可以用CURL_ZERO_TERMINATED而是要實際算出圖片的資料長度。
可以參考官方的範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
curl_mime *mime;
curl_mimepart *part;

/* create a mime handle */
mime = curl_mime_init(easy);

/* add a part */
part = curl_mime_addpart(mime);

/* send image data from memory */
curl_mime_data(part, imagebuf, imagebuf_len);

/* set a file name to make it look like a file upload */
curl_mime_filename(part, "image.png");

/* set name */
curl_mime_name(part, "data");

讀binary file

直接將圖片讀進記憶體內不要做任何處理
範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
void ReadFile(char *name)
{
FILE *file;
char *buffer;
unsigned long fileLen;

//Open file
file = fopen(name, "rb");
if (!file)
{
fprintf(stderr, "Unable to open file %s", name);
return;
}

//Get file length
fseek(file, 0, SEEK_END);
fileLen=ftell(file);
fseek(file, 0, SEEK_SET);

//Allocate memory
buffer=(char *)malloc(fileLen+1);
if (!buffer)
{
fprintf(stderr, "Memory error!");
fclose(file);
return;
}

//Read file contents into buffer
fread(buffer, fileLen, 1, file);
fclose(file);

//Do what ever with buffer

free(buffer);
}

termshark

再除錯的過程中,最好可以直接看一看自己的封包長什麼樣子,對於沒有螢幕的Ubuntu Server,可以安裝termshark,它可以做到跟wireshark差不多的事情

完整範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
/********* Sample code generated by the curl command line tool **********
* All curl_easy_setopt() options are documented at:
* https://curl.haxx.se/libcurl/c/curl_easy_setopt.html
************************************************************************/
#include <curl/curl.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
CURLcode ret;
CURL *hnd;
curl_mime *mime1;
curl_mimepart *part1;
struct curl_slist *slist1;

mime1 = NULL;
slist1 = NULL;
slist1 = curl_slist_append(slist1, "accept: application/json");
slist1 = curl_slist_append(slist1, "Content-Type: multipart/form-data");

hnd = curl_easy_init();
curl_easy_setopt(hnd, CURLOPT_BUFFERSIZE, 102400L);


curl_easy_setopt(hnd, CURLOPT_URL, "http://localhost:8000/upload");


curl_easy_setopt(hnd, CURLOPT_NOPROGRESS, 1L);
mime1 = curl_mime_init(hnd);
part1 = curl_mime_addpart(mime1);


// https://www.w3schools.blog/c-read-binary-file
FILE *file;
const char *buffer;
unsigned long fileLen;

//Open file
file = fopen("image.jpg", "rb");
if (!file)
{
fprintf(stderr, "Unable to open file");
return;
}

//Get file length
fseek(file, 0, SEEK_END);
fileLen=ftell(file);
fseek(file, 0, SEEK_SET);

//Allocate memory
buffer=(char *)malloc(fileLen+1);
if (!buffer)
{
fprintf(stderr, "Memory error!");
fclose(file);
return;
}

//Read file contents into buffer
fread(buffer, fileLen, 1, file);
fclose(file);

/* add data to the part */
curl_mime_data(part1, buffer, fileLen);

/* set a file name to make it look like a file upload */
curl_mime_filename(part1, "image.jpg");

free(buffer);


curl_mime_name(part1, "uploadedFile");
curl_mime_type(part1, "image/jpeg");
part1 = curl_mime_addpart(mime1);
curl_mime_data(part1, "2022-02-02", CURL_ZERO_TERMINATED);
curl_mime_name(part1, "EventTime");
curl_easy_setopt(hnd, CURLOPT_MIMEPOST, mime1);
curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, slist1);
curl_easy_setopt(hnd, CURLOPT_USERAGENT, "curl/7.68.0");
curl_easy_setopt(hnd, CURLOPT_MAXREDIRS, 50L);
curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, (long)CURL_HTTP_VERSION_2TLS);
curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(hnd, CURLOPT_FTP_SKIP_PASV_IP, 1L);
curl_easy_setopt(hnd, CURLOPT_TCP_KEEPALIVE, 1L);


ret = curl_easy_perform(hnd);

curl_easy_cleanup(hnd);
hnd = NULL;
curl_mime_free(mime1);
mime1 = NULL;
curl_slist_free_all(slist1);
slist1 = NULL;

return (int)ret;
}
/**** End of sample code ****/

參考:

https://daniel.haxx.se/blog/2022/09/12/convert-a-curl-cmdline-to-libcurl-source-code/

https://curl.se/libcurl/c/curl_mime_filename.html

https://geekscripts.guru/termshark-terminal-ui-for-tshark/

Author

Steven

Posted on

2023-03-22

Updated on

2025-01-14

Licensed under

Comments