乐于分享
好东西不私藏

如何实现固件下载不断线

如何实现固件下载不断线

在嵌入式设备固件升级、大文件下载场景中,网络不稳定导致下载中断后重新下载整个文件,浪费时间与带宽。

断点续传原理

关键HTTP头字段

头字段
类型
作用
示例值
Accept-Ranges
响应头
服务器声明是否支持范围请求
bytes
Range
请求头
客户端请求指定范围的字节
bytes=1024-2047
Content-Range
响应头
服务器返回当前响应的数据范围
bytes 1024-2047/50000
Content-Length
响应头
当前响应的数据长度
1024

交互流程

客户端                              服务器  |                                   |  |-------- HEAD /file.zip --------->|  检查是否支持断点续传  |<------- Accept-Ranges: bytes ----|  |<------- Content-Length: 50000 ---|  |                                   |  |-------- Range: bytes=0-1023 ---->|  请求前1024字节  |<------- Content-Range: bytes 0-1023/50000 ----|  |<------- [1024字节数据] ----------|  |                                   |  |-------- Range: bytes=1024- ----->|  从1024字节开始续传  |<------- Content-Range: bytes 1024-49999/50000 ----|  |<------- [剩余数据] ---------------|

curl断点续传

自动续传

使用-C -选项,curl自动检测本地已下载文件大小,从断点处继续下载。

curl -C - -O http://example.com/firmware.bin

原理

  1. 1. 检查本地是否存在firmware.bin
  2. 2. 获取本地文件大小(如已下载2048字节)
  3. 3. 发送请求:Range: bytes=2048-
  4. 4. 服务器返回206 Partial Content状态码
  5. 5. 将新数据追加到本地文件末尾

手动指定续传位置

使用-C <offset>指定从特定字节位置开始下载。

curl -C 1024 -o firmware.bin http://example.com/firmware.bin

适用场景

  • • 本地文件损坏,需要从特定位置重新下载
  • • 已知准确的断点位置
  • • 需要精确控制下载范围

范围下载

使用--range-r选项下载指定范围的数据。

curl --range 0-1048575 -o part1.bin http://example.com/largefile.bincurl --range 1048576-2097151 -o part2.bin http://example.com/largefile.bin

适用场景

  • • 多线程分段下载
  • • 下载文件的特定部分
  • • 并行下载加速

具体实现

嵌入式设备固件升级

在嵌入式Linux设备上实现可靠的固件下载:

#!/bin/bashFIRMWARE_URL="http://ota.example.com/firmware_v2.1.bin"FIRMWARE_FILE="firmware.bin"MAX_RETRY=5RETRY_COUNT=0while [ $RETRY_COUNT -lt $MAX_RETRY ]; doecho"下载尝试: $((RETRY_COUNT + 1))/$MAX_RETRY"    curl -C - --connect-timeout 30 --max-time 600 \         -o "$FIRMWARE_FILE""$FIRMWARE_URL"    CURL_EXIT_CODE=$?if [ $CURL_EXIT_CODE -eq 0 ]; thenecho"下载完成"breakelif [ $CURL_EXIT_CODE -eq 18 ]; thenecho"部分传输完成,准备续传..."        RETRY_COUNT=$((RETRY_COUNT + 1))sleep 5elseecho"下载失败,错误码: $CURL_EXIT_CODE"exit 1fidoneif [ $RETRY_COUNT -eq $MAX_RETRY ]; thenecho"达到最大重试次数,下载失败"exit 1fiecho"验证文件完整性..."md5sum -c firmware.md5

参数说明

  • • --connect-timeout 30:连接超时30秒
  • • --max-time 600:总传输时间不超过10分钟
  • • -C -:自动续传
  • • 退出码18:表示传输中断但部分数据已保存

大文件分段下载

对于超大文件(如系统镜像),使用分段下载提高可靠性:

#!/bin/bashURL="http://mirror.example.com/ubuntu-22.04.iso"OUTPUT="ubuntu-22.04.iso"TOTAL_SIZE=$(curl -sI "$URL" | grep -i "Content-Length" | awk '{print $2}' | tr -d '\r')CHUNK_SIZE=$((10 * 1024 * 1024))  # 10MB每块echo"文件总大小: $TOTAL_SIZE 字节"rm -f "$OUTPUT"touch"$OUTPUT"for ((offset=0; offset<TOTAL_SIZE; offset+=CHUNK_SIZE)); do    end=$((offset + CHUNK_SIZE - 1))if [ $end -ge $TOTAL_SIZE ]; then        end=$((TOTAL_SIZE - 1))fiecho"下载范围: $offset-$end"    curl --range "$offset-$end" -s -o temp_chunk "$URL"cat temp_chunk >> "$OUTPUT"rm -f temp_chunkdoneecho"下载完成,验证文件大小..."ACTUAL_SIZE=$(stat -c%s "$OUTPUT")if [ "$ACTUAL_SIZE" -eq "$TOTAL_SIZE" ]; thenecho"文件大小验证通过"elseecho"文件大小不匹配: 期望 $TOTAL_SIZE, 实际 $ACTUAL_SIZE"fi

检测服务器是否支持断点续传

#!/bin/bashURL="http://example.com/file.zip"SUPPORT_RANGE=$(curl -sI "$URL" | grep -i "Accept-Ranges" | awk '{print $2}' | tr -d '\r')if [ "$SUPPORT_RANGE" = "bytes" ]; thenecho"服务器支持断点续传"    FILE_SIZE=$(curl -sI "$URL" | grep -i "Content-Length" | awk '{print $2}' | tr -d '\r')echo"文件大小: $FILE_SIZE 字节"    curl -C - -O "$URL"elseecho"服务器不支持断点续传,使用普通下载"    curl -O "$URL"fi

libcurl编程实现

在嵌入式C程序中使用libcurl实现断点续传:

#include<stdio.h>#include<curl/curl.h>#include<sys/stat.h>size_twrite_data(void *ptr, size_t size, size_t nmemb, FILE *stream) {return fwrite(ptr, size, nmemb, stream);}intdownload_with_resume(constchar *url, constchar *output_file) {    CURL *curl;    FILE *fp;    CURLcode res;long file_size = 0;structstatst;if (stat(output_file, &st) == 0) {        file_size = st.st_size;    }    curl = curl_easy_init();if (!curl) {return-1;    }    fp = fopen(output_file, "ab");if (!fp) {        curl_easy_cleanup(curl);return-1;    }    curl_easy_setopt(curl, CURLOPT_URL, url);    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);    curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);    curl_easy_setopt(curl, CURLOPT_RESUME_FROM, file_size);    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30L);    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 600L);    res = curl_easy_perform(curl);    fclose(fp);    curl_easy_cleanup(curl);return (res == CURLE_OK) ? 0 : -1;}intmain() {constchar *url = "http://example.com/firmware.bin";constchar *output = "firmware.bin";    curl_global_init(CURL_GLOBAL_DEFAULT);if (download_with_resume(url, output) == 0) {printf("下载完成\n");    } else {printf("下载失败\n");    }    curl_global_cleanup();return0;}

编译命令

gcc -o download download.c -lcurl

API说明

  • • CURLOPT_RESUME_FROM:设置续传起始位置
  • • CURLOPT_CONNECTTIMEOUT:连接超时时间
  • • CURLOPT_TIMEOUT:总传输超时时间