nginx 自动解压gzip压缩数据的两种方式:nginx自动解压、后台java程序解压

日萌社

人工智能AI:Keras PyTorch MXNet TensorFlow PaddlePaddle 深度学习实战(不定时更新)


nginx+keepalived:局域网内网和公网外网 搭建nginx HA 双机热备 高可用

nginx 把 POST请求数据 写入到 日志文件中

nginx 自动解压gzip压缩数据的两种方式:nginx自动解压、后台java程序解压

nginx安装、nginx反向代理/负载均衡、Lua、LuaJIT、openresty、lua-nginx-module、ngx_devel_kit 的安装

nginx 配置获取GET请求参数、POST请求参数、nginx配置开启跨域访问、nginx+keepalived配置主备切换/双机热备、nginx优化配置


注意:在配置nginx 自动解压gzip压缩数据之前,需要安装以下软件、模块

链接:nginx、Lua、LuaJIT、openresty、lua-nginx-module、ngx_devel_kit 的安装

=============================================

文章参考:

https://huoding.com/2013/09/02/283

https://blog.csdn.net/qingchezhanlandetian/article/details/50327111

http://www.pataliebre.net/howto-make-nginx-decompress-a-gzipped-request.html#.XMKhhjMzaHv

https://blog.csdn.net/chenglian1987/article/details/78502246

=============第一种方式:nginx自动解压====================


vim /root/softs/nginx/conf/nginx.conf 

   server 
    {
        location /unzip/ {

            proxy_pass http://backend;
            proxy_set_header        REMOTE_ADDR       $remote_addr;
            proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;

            proxy_next_upstream http_500 http_502  http_504;
            limit_req   zone=java  burst=5 nodelay;
            #proxy_next_upstream_tries 1;

            set $max_chunk_size 10240;  # Chunks of 10 KB
            set $max_body_size 524288;  # Max inflated body size of 512 KB

            rewrite_by_lua_file /root/b.lua;
        }
    }

b.lua

    local zlib = require "zlib"

    local encoding = ngx.req.get_headers()["Content-Encoding"]

    if encoding == "gzip" then
        local body = ngx.req.get_body_data()

        if body then
            local stream = zlib.inflate()
            ngx.req.set_body_data(stream(body))
        end
    end

curl -v -s --trace-ascii http_post_trace.log --data-binary @gzip_request_test.gz -H "Content-Type: text/html" -H "Content-Encoding: gzip" -o response.txt -X POST http://192.168.88.102:80/unzip/

参数解析:
        curl --trace-ascii output.text www.taobao.com # 显示ascii细节
        curl -o baidu.html www.baidu.com # 文件保存
        curl --header "Content-Type:application/json" http://example.com # 添加头
        -X 这个参数可以配置HTTP的方法:
            curl -X POST 127.0.0.1:7001/api/material/update
            curl -X POST --data "name=kk&name2=gg" 127.0.0.1:7001/api/material/create # 带参数的post提交

        默认get:curl http://xxxx 
        post 文件:curl --data-binary @/tmp/xxx  http://xxxx
        更新文件(url中指定需要更新的文件名):curl -X PUT  --data-binary @/tmp/xxx  http://xxxx 
        删除(url中指定需要删除的文件名):curl -X DELETE http://xxxx

        Curl命令提供了若干个设置HTTP POST数据的选项,这里比较如下:
            官方文档:https://curl.haxx.se/docs/manpage.html
            网友介绍:https://blog.csdn.net/taiyangdao/article/details/77020762
            
            -d,--data <key=value>
                    将HTTP POST请求中的数据发送给HTTP服务器,与用户提交HTML表单时浏览器的行为完全一样。
                    默认Content-type为application/x-www-form-urlencoded
                    @file_name:表示数据来自一个文件,文件中的回车符和换行符将被转换
         
            --data-ascii <key=value>
                    等价于-d


========================================================

zlib库不能直接压缩gzip格式,使用lua-ffi-zlib

源码路径:https://github.com/hamishforbes/lua-ffi-zlib

调用:

local ffi_zlib = require "lib.ffi-zlib"

local chunk = 16384
    local count = 0 
    
    local input = function(bufsize)  
        local start = count > 0 and bufsize*count or 1  
        local data = str:sub(start, (bufsize*(count+1)-1))  
        if data == "" then  
            data = nil  
        end
        print(data)
        count = count + 1  
        return data  
    end 
    local output_table = {}  
    local output = function(data)
        insert(output_table, data)
    end 
    local ok, err = ffi_zlib.deflateGzip(input, output, chunk)  
    if not ok then  
        print(err)
    end  
    local compress = concat(output_table,'')  
    ngx.header["Content-Encoding"] = "gzip"

ngx.print(compress)


========================================================
方案
第一个选择是使用lua-zlib:

local zlib = require "zlib"

local encoding = ngx.req.get_headers()["Content-Encoding"]

if encoding == "gzip" then
    local body = ngx.req.get_body_data()

    if body then
        local stream = zlib.inflate()
        ngx.req.set_body_data(stream(body))
    end
end

第二个选择是通过LuaJIT的FFI库来包装ZLIB模块,官方教程里有一些现成的可供参考的的例子,不过例子里介绍的是Deflate,而不是Gzip,自己用FFI封装Gzip的话又有点小复杂,好在别人已经做了相关的工作,那就是lua-files:

local ffi  = require "ffi"
local zlib = require "zlib"

local function reader(s)
    local done
    return function()
        if done then return end
        done = true
        return s
    end
end

local function writer()
    local t = {}
    return function(data, sz)
        if not data then return table.concat(t) end
        t[#t + 1] = ffi.string(data, sz)
    end
end

local encoding = ngx.req.get_headers()["Content-Encoding"]

if encoding == "gzip" then
    local body = ngx.req.get_body_data()

    if body then
        local write = writer()
        zlib.inflate(reader(body), write, nil, "gzip")
        ngx.req.set_body_data(write())
    end
end


如上例子代码源自zlib_test.lua,乍看上去,代码里的reader和writer可能会令人费解,其实你可以把它们理解成输入输出接口,可以修改成文件,数据库等等形式。

别高兴太早,当你运行时,很可能会遇到如下错误:

libzlib.so: cannot open shared object file.

实际上这是因为如下zlib.lua代码的缘故:

local C = ffi.load 'zlib'
运行时,ffi.load会自动补全文件名,如果是Windows,则加载zlib.dll文件,如果是Linux,则加载libzlib.so,但实际上在Linux下,ZLIB扩展的名字是libz.so,而非libzlib.so。

知道的问题的原委,我们自然就知道如何修改代码了:

local C

if ffi.os == "Windows" then
    C = ffi.load "zlib"
else
    C = ffi.load "z"
end


有时候我们不推荐直接修改第三方库的代码,因为这样的话,每次第三库更新代码,我们都要做对应的修改,一旦忘记就会出错,这时候可以考虑做一个软连接别名。

测试
开篇说过,接口都是用PHP做的,不过请求里的Gzip数据是用LUA处理的,如何让PHP使用LUA处理后的数据呢?不同的语言似乎是个难题,好在Nginx有Phases一说,PHP作为FastCGI模块工作在content阶段,LUA可以工作在access阶段,这样它们就和谐了:

location ~ \.php$ {
    access_by_lua_file /path/to/lua/file;

    include fastcgi.conf;
    fastcgi_pass 127.0.0.1:9000;
}


 

=============第一种方式:后台java程序解压====================
 

java gzip压缩/ungzip解压缩、base64 encode编码/decode编码、URLEncode.encode编码/URLDecoder.decode编码

 

代码文件下载链接: https://pan.baidu.com/s/19GIva6_AKlFdbVDxbYqLEw
提取码: m6xj

 

package com.nginxlog;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URLDecoder;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

public class GZIPUtils
{
    public static final String GZIP_ENCODE_UTF_8 = "UTF-8";
    public static final String GZIP_ENCODE_ISO_8859_1 = "ISO-8859-1";


    public static byte[] compress(String str, String encoding)
//    public static byte[] compress(String str, String encoding)
    {
        if (str == null || str.length() == 0) {
            return null;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream(str.getBytes().length);
        GZIPOutputStream gzip;
        try {
            gzip = new GZIPOutputStream(out);

            gzip.write(str.getBytes(encoding));
//            byte[] bytes = out.toByteArray();

            gzip.close();
        } catch ( Exception e) {
            e.printStackTrace();
        }

        byte[] bytes = out.toByteArray();
        try
        {
            out.close();
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        return bytes;
//        return new String(Base64Coder.encode(bytes));//Base64Coder 对 压缩数据 进行 编码
     }

    public static byte[] compress(String str) throws IOException
    {
        return compress(str, GZIP_ENCODE_UTF_8);
    }

    public static byte[] uncompress(byte[] bytes)
     {
        int bufferSize = 4096;
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream(bytes.length);
        ByteArrayInputStream in = new ByteArrayInputStream(bytes);
        try {
            GZIPInputStream ungzip = new GZIPInputStream(in,bufferSize);
            byte[] buffer = new byte[bufferSize];
            int n;
            while ((n = ungzip.read(buffer)) >= 0) {
                out.write(buffer, 0, n);
            }
            ungzip.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        byte[] bytes1 = out.toByteArray();

        try
        {
            in.close();
            out.close();
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        return bytes1;
    }

    public static String uncompressToString(byte[] bytes, String encoding) throws IOException {
        int bufferSize = 4096;
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream(bytes.length);
        ByteArrayInputStream in = new ByteArrayInputStream(bytes);
        GZIPInputStream ungzip = null;

        ungzip = new GZIPInputStream(in,bufferSize);
        byte[] buffer = new byte[bufferSize];
        int n;
        while ((n = ungzip.read(buffer)) > 0)
        {
            out.write(buffer, 0, n);
            out.flush();
        }

        if (ungzip!=null)
        {
            ungzip.close();
        }
        in.close();
        String s = out.toString(encoding);
        out.close();
        return s;
    }

    public static String uncompressToString(byte[] bytes) throws IOException {
        return uncompressToString(bytes, GZIP_ENCODE_UTF_8);
    }

//    public static void main(String[] args) throws IOException
//    {
//        String s = "hello world";
//        System.out.println("字符串长度:"+s.length());
//        System.out.println("压缩后::"+compress(s));
//        System.out.println("解压后:"+uncompress(compress(s)));
//        System.out.println("解压字符串后:"+uncompressToString(compress(s)));
//        System.out.println("解压字符串后的字符串长度:"+uncompressToString(compress(s)).length());
//
//
//        String tmp = "H4sIAAAAAAAAAI2Tv47UMBDGXwWZK5fI/xLb221FRYWEkBCKbM+YWLebhMSb1ep0BdUVFIgKHoGWhzqJewuc3J5YEIdIE83P+Wa+8UzeXJE6DdZf1hHI+hkrKWXMyEquSIo7JGtWlpWhUkrKK5rhsc+QLBKyIhDHFFufFjUx3nFhgdMscT4HhjtDQQQqmWCcM4FOaA/KciiNsR5LLx1w44wCqgx41FKL0nihUILiPASqBNcqGD8nARRSW3BaOMXLoLiVQA1jFE3lwYCujDAWjFBQBaOsE9l7cCJU3gSRy6BgUlShcpUMNmjUIZvwnjGlfWVyQ9voyPqKXCxvsmlh6HJrqwXUEw5j7Np8IApalDO2fX+GWcEfvt1harr5UnwH+AABk43bBe4KZ1PhG5u2ccL3hfUpTjEdixc2tptT8HR5yPWK4IRtysKLTd+/inh46QfENufth67HIUUc/+67G8/86YIVdKaAU/R4PzXQxgHKUJZUehvYP7rd5V5m+1OcuievOdvcV/i94rhYqw8RUpP3h2r6CzYY3zW5D84XurPtPk8g7QccTmkfu9SZtXbeSHJ38/n2+8cfXz7dfru5+/phVhxii";
//        byte[] bytes = tmp.getBytes("ISO8859-1");
//        String s1 = new String(bytes, "UTF-8");
//        String urldecode = URLDecoder.decode(s1, "UTF-8");
//        byte[] decode = Base64Coder.decode(urldecode);
//        String s2 = GZIPUtils.uncompressToString(decode);
//        System.out.println(s2);
//    }
}

 

----------------------------base64转码------------------------------

package com.nginxlog;


public class Base64Coder
{
    // Mapping table from 6-bit nibbles to Base64 characters.
    private static char[] map1 = new char[64];

    static {
        int i = 0;
        for (char c = 'A'; c <= 'Z'; c++)
            map1[i++] = c;
        for (char c = 'a'; c <= 'z'; c++)
            map1[i++] = c;
        for (char c = '0'; c <= '9'; c++)
            map1[i++] = c;
        map1[i++] = '+';
        map1[i++] = '/';
    }

    // Mapping table from Base64 characters to 6-bit nibbles.
    private static byte[] map2 = new byte[128];

    static {
        for (int i = 0; i < map2.length; i++)
            map2[i] = -1;
        for (int i = 0; i < 64; i++)
            map2[map1[i]] = (byte) i;
    }

    /**
     * Encodes a string into Base64 format.
     * No blanks or line breaks are inserted.
     *
     * @param s a String to be encoded.
     * @return A String with the Base64 encoded data.
     */
    public static String encodeString(String s) {
        return new String(encode(s.getBytes()));
    }

    /**
     * Encodes a byte array into Base64 format.
     * No blanks or line breaks are inserted.
     *
     * @param in an array containing the data bytes to be encoded.
     * @return A character array with the Base64 encoded data.
     */
    public static char[] encode(byte[] in) {
        return encode(in, in.length);
    }

    /**
     * Encodes a byte array into Base64 format.
     * No blanks or line breaks are inserted.
     *
     * @param in   an array containing the data bytes to be encoded.
     * @param iLen number of bytes to process in <code>in</code>.
     * @return A character array with the Base64 encoded data.
     */
    public static char[] encode(byte[] in, int iLen) {
        int oDataLen = (iLen * 4 + 2) / 3;       // output length without padding
        int oLen = ((iLen + 2) / 3) * 4;         // output length including padding
        char[] out = new char[oLen];
        int ip = 0;
        int op = 0;
        while (ip < iLen) {
            int i0 = in[ip++] & 0xff;
            int i1 = ip < iLen ? in[ip++] & 0xff : 0;
            int i2 = ip < iLen ? in[ip++] & 0xff : 0;
            int o0 = i0 >>> 2;
            int o1 = ((i0 & 3) << 4) | (i1 >>> 4);
            int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6);
            int o3 = i2 & 0x3F;
            out[op++] = map1[o0];
            out[op++] = map1[o1];
            out[op] = op < oDataLen ? map1[o2] : '=';
            op++;
            out[op] = op < oDataLen ? map1[o3] : '=';
            op++;
        }
        return out;
    }

    /**
     * Decodes a string from Base64 format.
     *
     * @param s a Base64 String to be decoded.
     * @return A String containing the decoded data.
     * @throws IllegalArgumentException if the input is not valid Base64 encoded data.
     */
    public static String decodeString(String s) {
        return new String(decode(s));
    }

    /**
     * Decodes a byte array from Base64 format.
     *
     * @param s a Base64 String to be decoded.
     * @return An array containing the decoded data bytes.
     * @throws IllegalArgumentException if the input is not valid Base64 encoded data.
     */
    public static byte[] decode(String s) throws IllegalArgumentException
    {
        return decode(s.toCharArray());
    }

    /**
     * Decodes a byte array from Base64 format.
     * No blanks or line breaks are allowed within the Base64 encoded data.
     *
     * @param in a character array containing the Base64 encoded data.
     * @return An array containing the decoded data bytes.
     * @throws IllegalArgumentException if the input is not valid Base64 encoded data.
     */
    public static byte[] decode(char[] in) {
        int iLen = in.length;
        if (iLen % 4 != 0)
            throw new IllegalArgumentException("Length of Base64 encoded input string is not a multiple of 4。base64编码输入字符串的长度不是4的倍数");
        while (iLen > 0 && in[iLen - 1] == '=')
            iLen--;
        int oLen = (iLen * 3) / 4;
        byte[] out = new byte[oLen];
        int ip = 0;
        int op = 0;
        while (ip < iLen) {
            int i0 = in[ip++];
            int i1 = in[ip++];
            int i2 = ip < iLen ? in[ip++] : 'A';
            int i3 = ip < iLen ? in[ip++] : 'A';
            if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127)
                throw new IllegalArgumentException("Illegal character in Base64 encoded data.");
            int b0 = map2[i0];
            int b1 = map2[i1];
            int b2 = map2[i2];
            int b3 = map2[i3];
            if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0)
                throw new IllegalArgumentException("Illegal character in Base64 encoded data.");
            int o0 = (b0 << 2) | (b1 >>> 4);
            int o1 = ((b1 & 0xf) << 4) | (b2 >>> 2);
            int o2 = ((b2 & 3) << 6) | b3;
            out[op++] = (byte) o0;
            if (op < oLen)
                out[op++] = (byte) o1;
            if (op < oLen)
                out[op++] = (byte) o2;
        }
        return out;
    }





} // end class Base64Coder

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

あずにゃん

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值