读取服务器上的图片大小,最可靠的做法是发送HTTP HEAD请求获取Content-Length响应头,或直接解析图片二进制头部中的宽高字段,二者选其一取决于你是只需要文件字节数,还是需要图片像素尺寸。
先搞清楚你要的“大小”是哪种
很多人在搜索引擎里查“c怎么读取服务器上的图片大小”,其实需求分两类,一类是图片文件的体积,单位是KB、MB,这个直接看HTTP响应头里的Content-Length就能拿到,另一类是图片的分辨率,比如1920×1080,这个得去解析图片文件本身的二进制格式,JPEG、PNG、GIF各自的尺寸信息存储位置完全不同。
判断标准很简单:下载图片还是只读信息? 如果你要把图片存到本地,顺便记录文件大小,那用Content-Length最省事,如果你要做缩略图、裁剪或者图片懒加载占位,那就必须拿到像素宽高,这时候无论服务器返回什么头信息都不靠谱,必须看图片数据本身。
HTTP HEAD请求拿文件体积
这是最直接的路径,用libcurl库几行代码就能搞定,libcurl是C语言处理HTTP事实标准,几乎所有Linux发行版都自带开发包。
核心代码逻辑
#include <curl/curl.h>
#include <stdlib.h>
#include <string.h>
size_t header_callback(char buffer, size_t size, size_t nitems, void userdata) {
long content_length = (long )userdata;
if (strncmp(buffer, "Content-Length:", 15) == 0) {
content_length = atol(buffer + 15);
}
return size nitems;
}
int main() {
CURL curl = curl_easy_init();
long file_size = 0;
curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/image.jpg");
curl_easy_setopt(curl, CURLOPT_NOBODY, 1L); // HEAD请求
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback);
curl_easy_setopt(curl, CURLOPT_HEADERDATA, &file_size);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);
CURLcode res = curl_easy_perform(curl);
if (res == CURLE_OK && file_size > 0) {
printf("图片大小: %ld 字节n", file_size);
} else {
fprintf(stderr, "请求失败或被服务器拒绝n");
}
curl_easy_cleanup(curl);
return 0;
}
这段代码用NOBODY选项发HEAD请求,不下载body内容,只收响应头,服务端返回的Content-Length就是图片文件的精确字节数,需要注意,
有些CDN或防盗链服务器不响应HEAD请求,会返回405,这时候得降级用GET请求加Range头只读第一个字节,看Content-Range里的总大小。
编译和测试命令
gcc -o fetch_size fetch_size.c -lcurl ./fetch_size
这个方案实测很快,因为传输量极小,通常一个RTT就能拿到结果,但要记住,HTTP头里的Content-Length可能被代理服务器篡改,或者因为服务端开启了chunked编码而不存在,行业共识认为,这个方案适合内网环境或者你完全掌控服务端配置的场景。
直接解析图片头部拿像素尺寸
如果你要知道图片的宽高,用纯C手工解析二进制格式是可行的,就是得对每种格式单独写解析器,这里以最常见的JPEG和PNG为例。
JPEG的SOF段扫描法
JPEG文件结构是段式存储,每段以0xFF开头,紧跟一个标记字节,我们需要的宽高信息存在SOF0(0xC0)到SOF3(0xC3)以及SOF5(0xC5)到SOF7(0xC7)这些段里。
int get_jpeg_size(const unsigned char buf, size_t len, int width, int height) {
if (len < 4 || buf[0] != 0xFF || buf[1] != 0xD8) return -1;
size_t pos = 2;
while (pos + 9 < len) {
if (buf[pos] != 0xFF) { pos++; continue; }
unsigned char marker = buf[pos + 1];
if (marker >= 0xC0 && marker <= 0xCF && marker != 0xC4 && marker != 0xC8 && marker != 0xCC) {
height = (buf[pos + 5] << 8) | buf[pos + 6];
width = (buf[pos + 7] << 8) | buf[pos + 8];
return 0;
}
unsigned short seg_len = (buf[pos + 2] << 8) | buf[pos + 3];
pos += 2 + seg_len;
}
return -2; // 未找到SOF段
}
这个逻辑在本地文件上验证过没问题,但如果图片经过EXIF旋转,读出来的宽高可能和实际显示方向相反,解析时需要额外判断Orientation标签。
PNG的IHDR块读取
PNG格式简单得多,文件头8字节固定,紧接着就是IHDR数据块,宽高直接以big-endian存着。
int get_png_size(const unsigned char buf, size_t len, int width, int height) {
if (len < 24) return -1;
if (buf[0] == 0x89 && buf[1] == 'P' &&
amp; buf[2] == 'N' && buf[3] == 'G') {
width = (buf[16] << 24) | (buf[17] << 16) | (buf[18] << 8) | buf[19];
height = (buf[20] << 24) | (buf[21] << 16) | (buf[22] << 8) | buf[23];
return 0;
}
return -1;
}
注意PNG的宽高是网络字节序(大端),千万别按小端读,否则数值完全不对。 这是新手最容易犯的错。
服务器上怎么用
上面两个函数都要求先拿到图片的二进制数据,在服务器场景里,常见做法是先发HTTP Range请求读前几百字节:
curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_RANGE, "0-1023"); // 只读前1KB
然后用拿到的缓冲区去调get_jpeg_size或get_png_size,据统计,大多数JPEG的SOF段都出现在文件前2KB以内,读1K基本够用,但极端情况可能得多读几次。
两种方案的对比选型
| 维度 | HEAD请求拿Content-Length | 解析图片头部拿像素 |
|——|————————–|——————-|| 文件字节数 | 宽高像素值 |
| 网络开销 | 极低,一个RTT | 略高,可能要GET 1-2KB数据 |
| 依赖库 | libcurl(或自己写socket) | 无,纯手工解析 |
| 适用场景 | 文件大小统计、下载进度条 | 前端占位、缩略图生成 |
| 坑点 | chunked编码、CDN不响应HEAD | 格式不完整、Exif旋转 |
还有个场景要注意:如果你要的是“服务器本地磁盘上图片文件的大小”,那直接用C标准库的stat()或fseek/ftell就行,不需要走HTTP,这常用于服务器运维监控脚本或者文件同步工具。
#include <sys/stat.h>
struct stat st;
if (stat("/var/www/images/photo.jpg", &st) == 0) {
printf("文件大小: %lld 字节n", (long long)st.st_size);
}
常见问题排查
为什么HEAD请求拿到的Content-Length是0
部分服务器开启了Transfer-Encoding: chunked,这种响应不会带Content-Length头,可以用curl的-v参数看响应头,确认是这个问题后,改用Content-Range方案,或者干脆发GET请求,用curl的CURLOPT_WRITEFUNCTION回调在收body时即时计数,到一定量就中断连接。
图片格式五花八门怎么办
生产环境不建议为每种格式手写解析器,用现成库是最省力的,比如libjpeg、libpng这些官方库,或者用GraphicsMagick的C API,几行代码就能拿到尺寸,而且处理畸形图片不会崩溃。
性能要求高的时候可以考虑,解析是纯内存运算,开销可以忽略不计。
服务器返回的是重定向
curl默认跟随重定向,但HeaderFunction回调会收到多个响应头,这种情况下Content-Length会被覆盖成最后一次响应的值,逻辑上没问题,但如果重定向到了HTML错误页,解析图片尺寸会失败,需要加状态码过滤。
实际项目里的代码组织方式
如果你要写一个完整的“服务器图片尺寸读取工具”,建议分成三层:网络层负责下载字节,解析层负责识别格式,业务层负责输出结果,客户端发起请求时,先用HEAO探测Content-Length,同时返回给前端一个预估的加载时间;等图片真正需要展示时,再异步解析像素尺寸,做懒加载占位,这个做法在很多图片托管服务里都能看到。
还有一点想提醒你,HTTP头和信息都不该被信任为图片真实尺寸,尤其是涉及用户上传内容的场景,服务端返回的Content-Length在理论上可以和数据进行交叉验证,但解析图片本身的二进制永远更可靠,社区共识是,宁可多读几个字节,也别省这点网络开销。
Q&A模块
问:c语言读取服务器图片大小,用libcurl和纯socket请求哪个更合适?
答:libcurl更合适,libcurl封装了TCP连接、HTTP协议解析、重定向、SSL/TLS等底层细节,代码量和出错率都远低于纯socket编程,除非你在嵌入式环境无法引入第三方库,否则没必要从零造轮子,纯socket方案需要自己处理HTTP状态码、Header边界、keep-alive、SSL握手,普通项目投入产出比很低。
问:Linux服务器上读图片尺寸,C语言和Python脚本比有什么优势?
答:C语言的优势在于编译产物无运行时依赖,部署到精简容器里不需要装Python解释器,内存占用也低得多,劣势是开发效率低,需要手动处理不同图片格式的二进制协议,如果图片来源可控,且服务器性能余量充足,用Python的Pillow库几行就能搞定,但你的场景如果是高并发图片处理网关,那C的稳定性和资源控制水平和Python不在一个数量级。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/708344.html





