You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于thttpd CGI的Bash后端如何正确处理二进制文件上传?

问题:Bash CGI后端无法正确获取上传的二进制文件

场景

后端基于thttpd运行CGI服务,新增文件上传功能,表单代码如下:

<form action="index.cgi" method="post" enctype="multipart/form-data">
    <input type="file" name="fileToUpload" id="fileToUpload">
    <input type="submit" value="Upload File" name="submit">
</form>

初始编写的Bash CGI代码:

if [ "$REQUEST_METHOD" == "POST" ]; then
while IFS= read -r line; do
printf "%s" "$line" >> encoded_file
done
fi

异常现象

文本类文件上传正常,但PNG等二进制文件出现损坏:

  • 保存的encoded_file包含multipart边界和HTTP头信息
  • 提取二进制内容后,与原始文件对比发现字节差异(如原始PNG的0D 0A被转为0A,部分空字节丢失)

保存的encoded_file部分十六进制

00000000   2D 2D 2D 2D  2D 2D 2D 2D  2D 2D 2D 2D  2D 2D 2D 2D  2D 2D 2D 2D  2D 2D 2D 2D  ------------------------
00000018   2D 2D 2D 2D  2D 31 36 39  33 34 34 35  39 32 39 31  34 35 30 36  31 30 30 36  -----1693445929145061006
00000030   35 34 31 34  31 35 32 32  31 32 0D 43  6F 6E 74 65  6E 74 2D 44  69 73 70 6F  5414152212.Content-Dispo
00000048   73 69 74 69  6F 6E 3A 20  66 6F 72 6D  2D 64 61 74  61 3B 20 6E  61 6D 65 3D  sition: form-data; name=
00000060   22 66 69 6C  65 54 6F 55  70 6C 6F 61  64 22 3B 20  66 69 6C 65  6E 61 6D 65  "fileToUpload"; filename
00000078   3D 22 49 4D  47 5F 37 30  39 37 2E 50  4E 47 22 0D  43 6F 6E 74  65 6E 74 2D  ="IMG_7097.PNG".Content-
00000090   54 79 70 65  3A 20 69 6D  61 67 65 2F  70 6E 67 0D  0D 89 50 4E  47 0D 1A 0D  Type: image/png...PNG...
000000A8   49 48 44 52  01 7B 03 93  08 06 5E B6  A3 CD 01 73  52 47 42 AE  CE 1C E9 38  IHDR.{....^....sRGB....8

提取后文件与原始文件对比

提取后的文件:

00000000   89 50 4E 47  0A 1A 0A 49  48 44 52 01  7B 03 93 08  06 5E B6 A3  CD 01 73 52  .PNG...IHDR.{....^....sR

原始文件:

00000000   89 50 4E 47  0D 0A 1A 0A  00 00 00 0D  49 48 44 52  00 00 01 7B  00 00 03 93  .PNG........IHDR...{....

更新:使用cat > encoded_file保存完整请求能保留空字节,但手动提取或用uudeview时仍丢失空字节,问题未解决。


原因分析

  1. read命令的局限性:read是为文本处理设计的,会自动将CRLF(0D 0A)转换为LF(0A),且遇到空字节(00)会终止读取,直接导致二进制文件损坏。
  2. multipart/form-data解析问题:直接保存的请求包含边界、HTTP头信息,提取时需要准确定位文件内容的起始和结束位置,且不能破坏原始二进制字节。
  3. 工具不兼容:uudeview这类工具针对UUEncoded数据设计,不适合直接解析multipart/form-data的二进制内容,会引发字节转换或丢失。

解决方案

方案1:用cat保存完整请求,再用awk精准提取文件内容

首先修改CGI代码,用cat保存完整POST请求(二进制安全):

if [ "$REQUEST_METHOD" = "POST" ]; then
    cat > full_request.bin
fi

然后用awk解析full_request.bin,提取文件内容:

# 从CONTENT_TYPE环境变量中提取boundary
BOUNDARY=$(echo "$CONTENT_TYPE" | sed -n 's/.*boundary=\([^;]*\).*/\1/p')

# 用awk跳过边界和头信息,提取核心文件内容
awk -v boundary="$BOUNDARY" '
    BEGIN {
        boundary_line = "--" boundary
        in_file = 0
    }
    $0 == boundary_line {
        in_file = 1
        next
    }
    in_file && $0 == "" {
        in_file = 2
        next
    }
    in_file == 2 {
        if ($0 == boundary_line "--") exit
        # 若需严格保留CRLF,替换为printf "%s\r\n", $0
        print
    }
' full_request.bin > uploaded_file.png

方案2:使用Python辅助脚本解析(更可靠)

编写Python脚本处理multipart/form-data的解析,避免Bash处理二进制的局限性:

# save_upload.py
import cgi
import sys

form = cgi.FieldStorage(fp=sys.stdin)
file_item = form.get('fileToUpload')
if file_item and hasattr(file_item, 'file'):
    with open('uploaded_file.png', 'wb') as f:
        f.write(file_item.file.read())

在Bash CGI中调用该脚本:

if [ "$REQUEST_METHOD" = "POST" ]; then
    python3 save_upload.py
fi

方案3:纯Bash用dd提取二进制内容(无需额外工具)

利用dd处理二进制数据,准确定位文件内容的起始和结束位置:

if [ "$REQUEST_METHOD" = "POST" ]; then
    # 提取boundary
    BOUNDARY=$(echo "$CONTENT_TYPE" | sed -n 's/.*boundary=\([^;]*\).*/\1/p')
    
    # 找到文件内容的起始行(跳过boundary和头信息)
    START=$(grep -a -n "^--$BOUNDARY$" full_request.bin | head -n1 | cut -d: -f1)
    START=$(($START + 4))  # 调整数值适配实际头信息行数
    
    # 找到结束boundary的位置
    END=$(grep -a -n "^--$BOUNDARY--$" full_request.bin | head -n1 | cut -d: -f1)
    END=$(($END - 1))
    
    # 提取内容
    dd if=full_request.bin of=uploaded_file.png skip=$START count=$(($END - $START)) bs=1 status=none
fi

注意:此方法依赖请求格式固定,若头信息行数变化需手动调整START的计算值。


内容的提问来源于stack exchange,提问作者Surepic 342

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 21:49:52