使用fphttpclient调用一个http接口,但接口返回的响应体是经gzip压缩过的。这本是一个很常用,也很基础的功能,不想却如历劫般充满磨难。
场景很常用,也很基础,本着能偷懒就偷懒的原则,向AI进行提问。
AI给出的答案是使用ZStream单元的Tdecompressionstream。但是,AI给的示例第二个传参的数据类型怎么和方法声明不一样?继续问AI,AI就道歉,再给示例。
本以为几轮勾通下来问题就解决了,不曾想,恶梦才刚刚开始。AI每次都先道歉,然后信誓旦旦的说这次肯定可以,结果每次都不行。到后来,AI一会说是版本问题,一会说是数据问题……其实那些问题一开始就是排除掉的,但AI就是死鸭子嘴硬,真是气得令人口吐芬芳。结果。到最后,这货果然开始摆烂、甩锅,说什么它只是个模型,反正它尽力了,它不知道,问他不如你自己去网上查靠谱……浪费一上午时间,结果就来个让自己去网上查,那要你AI有个卵用!
午饭后,换个方式,先让AI去官方的gitlab查下有没有相关bug,结果果然有!然后顺藤摸瓜再查下解决方案,但是没有查到明确的解决方式,反倒是AI又开始一本正经胡说八道了,一不小心,半下午又没了!
罢了,一直被这货瞎忽悠,还不如自己静下心来深入了解下。一番了解加测试后,发现是Tdecompressionstream并不能识别gzip的格式。
既然问题出在gzip的格式上,那就先简单了解下gzip格式吧,懒得自己去搜,又问了AI,这次倒是说得有模有样,而且和测试的数据也都对得上,于是就追问了下如何解决。结果,又不知不觉被这货一本正经胡说八道忽悠瘸了,等发觉时已经深夜了。
劫中劫#
官方的不能直接用,那就自己写个能用的。Tdecompressionstream是基于zlib的,而且有现成的底层功能可调用,那就仿一个支持gzip的,思路也很简单:把gzip的特征剥离后,剩下的就是原始的deflate,这个zlib肯定是支持的。
历经磨难,终于还是没写出能用的,看来一知半解就去搞还是行不通的,总不能像当初搞sm3、sm4那样对着规范研究几天再说吧!
对啊!虽说当初确实研究了好几天规范,但最终的实现还是参考了现成的c代码,fpc是小众中的小众,但c却是软件世界的基石啊,写pascal的话AI会一本正经胡说八道,那写c应该就只是代码搬运了吧。
让AI给了基于zlib的解压gzip的示例,自己对照着翻译成pascal,结果果然成功了!
仅是解压http的响应,够用了:
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
|
uses
ZBase, PasZLib;
function GZipDecompression(Input, Output: TStream): boolean;
var
z: z_stream;
ret: integer;
inBuf, outBuf: TBytes;
n: uint64;
offset: integer;
xlen: uint16;
begin
Result := False;
//数据太短
if Input.Size < 18 then
Exit;
SetLength(inBuf, Input.Size);
Input.Position := 0;
Input.Read(inBuf[0], Input.Size);
//检查GZip魔数
if (inBuf[0] <> $1F) or (inBuf[1] <> $8B) then
Exit;
//不支持的压缩方法
if inBuf[2] <> $08 then
Exit;
offset := 10;
//额外字段
if (inBuf[3] and $04) <> 0 then
begin
if offset + 2 > Input.Size then
Exit;
xlen := inBuf[offset] or (inBuf[offset + 1] shl 8);
offset := offset + 2 + xlen;
end;
//文件名
if (inBuf[3] and $08) <> 0 then
begin
while (offset < Input.Size) and (inBuf[offset] <> 0) do
Inc(offset);
if offset < Input.Size then //跳过0尾
Inc(offset);
end;
//注释
if (inBuf[3] and $10) <> 0 then
begin
while (offset < Input.Size) and (inBuf[offset] <> 0) do
Inc(offset);
if offset < Input.Size then //跳过0尾
Inc(offset);
end;
//头部CRC
if (inBuf[3] and $02) <> 0 then
begin
offset := offset + 2;
end;
//头部错误
if offset >= Input.Size then
Exit;
//解压后大小
Input.Position := Input.Size - 4;
Input.Read(n, 4);
SetLength(outBuf, n);
FillChar(z, SizeOf(z), 0);
//按原始deflate解压
ret := inflateInit2(z, -MAX_WBITS);
if ret <> Z_OK then
Exit;
z.avail_in := Input.Size - offset - 8;
z.next_in := @inBuf[offset];
z.avail_out := n;
z.next_out := @outBuf[0];
ret := inflate(z, Z_FINISH);
if ret <> Z_STREAM_END then
begin //解压失败
inflateEnd(z);
Exit;
end;
n := z.total_out;
inflateEnd(z);
Output.Write(outBuf[0], n);
Result := True;
end;
|