[
  {
    "path": "README.md",
    "content": "nginx-lua-fastdfs-GraphicsMagick\n==================\nfastdfs开源的分布式文件系统，此脚本利用nginx lua模块，动态生成图片缩略图，fastdfs只存一份原图。lua通过socket获取fastdfs的原图，并存放到本地，根据不同规则url，例如：_60x60.jpg、_80x80.jpg，类似淘宝图片url规则。利用gm命令生成本地缩略图，第二次访问直接返回本地图片。定时任务凌晨清除7天内未访问的图片，节省空间。\n\n图片访问举例\n----------------\n1. [http://192.168.1.113/group1/M00/00/00/wKgBcVN0wDiAILQXAAdtg6qArdU189.jpg](http://192.168.1.113/group1/M00/00/00/wKgBcVN0wDiAILQXAAdtg6qArdU189.jpg)\n2. [http://192.168.1.113/group1/M00/00/00/wKgBcVN0wDiAILQXAAdtg6qArdU189.jpg_80x80.jpg](http://192.168.1.113/group1/M00/00/00/wKgBcVN0wDiAILQXAAdtg6qArdU189.jpg_80x80.jpg)\n3. [http://gi1.md.alicdn.com/imgextra/i1/401612253/T2ASPfXE4XXXXXXXXX_!!401612253.jpg_60x60.jpg](http://gi1.md.alicdn.com/imgextra/i1/401612253/T2ASPfXE4XXXXXXXXX_!!401612253.jpg_60x60.jpg)\n4. [http://gi1.md.alicdn.com/imgextra/i1/401612253/T2ASPfXE4XXXXXXXXX_!!401612253.jpg_80x80.jpg](http://gi1.md.alicdn.com/imgextra/i1/401612253/T2ASPfXE4XXXXXXXXX_!!401612253.jpg_80x80.jpg)\n\n\n参考网址\n----------------\n1. [https://github.com/openresty/lua-nginx-module](https://github.com/openresty/lua-nginx-module)\n2. [https://github.com/azurewang/Nginx_Lua-FastDFS](https://github.com/azurewang/Nginx_Lua-FastDFS)\n3. [https://github.com/azurewang/lua-resty-fastdfs](https://github.com/azurewang/lua-resty-fastdfs)\n4. [http://rhomobi.com/topics/23](http://rhomobi.com/topics/23)\n5. [http://bbs.chinaunix.net/thread-4133106-1-1.html](http://bbs.chinaunix.net/thread-4133106-1-1.html)\n"
  },
  {
    "path": "crontab.sh",
    "content": "# 凌晨2点执行，查找目录下面7天内没有被访问的文件并删除，释放空间\n0 2 * * * find /data/images -atime -7 | xargs rm -rf\n"
  },
  {
    "path": "lua/fastdfs.lua",
    "content": "-- 写入文件\nlocal function writefile(filename, info)\n    local wfile=io.open(filename, \"w\") --写入文件(w覆盖)\n    assert(wfile)  --打开时验证是否出错\t\t\n    wfile:write(info)  --写入传入的内容\n    wfile:close()  --调用结束后记得关闭\nend\n\n-- 检测路径是否目录\nlocal function is_dir(sPath)\n    if type(sPath) ~= \"string\" then return false end\n\n    local response = os.execute( \"cd \" .. sPath )\n    if response == 0 then\n        return true\n    end\n    return false\nend\n\n-- 检测文件是否存在\nlocal file_exists = function(name)\n    local f=io.open(name,\"r\")\n    if f~=nil then io.close(f) return true else return false end\nend\n\nlocal area = nil\nlocal originalUri = ngx.var.uri;\nlocal originalFile = ngx.var.file;\nlocal index = string.find(ngx.var.uri, \"([0-9]+)x([0-9]+)\");  \nif index then \n    originalUri = string.sub(ngx.var.uri, 0, index-2);  \n    area = string.sub(ngx.var.uri, index);  \n    index = string.find(area, \"([.])\");  \n    area = string.sub(area, 0, index-1);  \n\n    local index = string.find(originalFile, \"([0-9]+)x([0-9]+)\");  \n    originalFile = string.sub(originalFile, 0, index-2)\nend\n\n-- check original file\nif not file_exists(originalFile) then\n    local fileid = string.sub(originalUri, 2);\n    -- main\n    local fastdfs = require('restyfastdfs')\n    local fdfs = fastdfs:new()\n    fdfs:set_tracker(\"192.168.1.113\", 22122)\n    fdfs:set_timeout(1000)\n    fdfs:set_tracker_keepalive(0, 100)\n    fdfs:set_storage_keepalive(0, 100)\n    local data = fdfs:do_download(fileid)\n    if data then\n       -- check image dir\n        if not is_dir(ngx.var.image_dir) then\n            os.execute(\"mkdir -p \" .. ngx.var.image_dir)\n        end\n        writefile(originalFile, data)\n    end\nend\n\n-- 创建缩略图\nlocal image_sizes = {\"80x80\", \"800x600\", \"40x40\", \"60x60\"};  \nfunction table.contains(table, element)  \n    for _, value in pairs(table) do  \n        if value == element then\n            return true  \n        end  \n    end  \n    return false  \nend \n\nif table.contains(image_sizes, area) then  \n    local command = \"gm convert \" .. originalFile  .. \" -thumbnail \" .. area .. \" -background gray -gravity center -extent \" .. area .. \" \" .. ngx.var.file;  \n    os.execute(command);  \nend;\n\nif file_exists(ngx.var.file) then\n    --ngx.req.set_uri(ngx.var.uri, true);  \n    ngx.exec(ngx.var.uri)\nelse\n    ngx.exit(404)\nend\n"
  },
  {
    "path": "lua/restyfastdfs.lua",
    "content": "-- Copyright (C) 2012 Azure Wang\n-- @link: https://github.com/azurewang/Nginx_Lua-FastDFS\n\nlocal string = string\nlocal table  = table\nlocal bit    = bit\nlocal ngx    = ngx\nlocal tonumber = tonumber\nlocal setmetatable = setmetatable\nlocal error = error\n\nmodule(...)\n\nlocal VERSION = '0.1'\n\nlocal FDFS_PROTO_PKG_LEN_SIZE = 8\nlocal TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ONE = 101\nlocal TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITH_GROUP_ONE = 104\nlocal TRACKER_PROTO_CMD_SERVICE_QUERY_UPDATE = 103\nlocal TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ONE = 102\nlocal STORAGE_PROTO_CMD_UPLOAD_FILE = 11\nlocal STORAGE_PROTO_CMD_DELETE_FILE = 12\nlocal STORAGE_PROTO_CMD_DOWNLOAD_FILE = 14\nlocal STORAGE_PROTO_CMD_UPLOAD_SLAVE_FILE = 21\nlocal STORAGE_PROTO_CMD_QUERY_FILE_INFO = 22\nlocal STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE = 23\nlocal STORAGE_PROTO_CMD_APPEND_FILE = 24\nlocal FDFS_FILE_EXT_NAME_MAX_LEN = 6\nlocal FDFS_PROTO_CMD_QUIT = 82\nlocal TRACKER_PROTO_CMD_RESP = 100\n\nlocal mt = { __index = _M }\n\nfunction new(self)\n    return setmetatable({}, mt)\nend\n\nfunction set_tracker(self, host, port)\n    local tracker = {host = host, port = port}\n    self.tracker = tracker\nend\n\nfunction set_timeout(self, timeout)\n    if timeout then\n        self.timeout = timeout\n    end\nend\n\nfunction set_tracker_keepalive(self, timeout, size)\n    local keepalive = {timeout = timeout, size = size}\n    self.tracker_keepalive = keepalive\nend\n\nfunction set_storage_keepalive(self, timeout, size)\n    local keepalive = {timeout = timeout, size = size}\n    self.storage_keepalive = keepalive\nend\n\nfunction int2buf(n)\n    -- only trans 32bit  full is 64bit\n    return string.rep(\"\\00\", 4) .. string.char(bit.band(bit.rshift(n, 24), 0xff), bit.band(bit.rshift(n, 16), 0xff), bit.band(bit.rshift(n, 8), 0xff), bit.band(n, 0xff))\nend\n\nfunction buf2int(buf)\n    -- only trans 32bit  full is 64bit\n    local c1, c2, c3, c4 = string.byte(buf, 5, 8)\n    return bit.bor(bit.lshift(c1, 24), bit.lshift(c2, 16),bit.lshift(c3, 8), c4)\nend\n\nfunction read_fdfs_header(sock)\n    local header = {}\n    local buf, err = sock:receive(10)\n    if not buf then\n        ngx.log(ngx.ERR, \"fdfs: read header error\")\n        sock:close()\n        ngx.exit(500)\n    end\n    header.len = buf2int(string.sub(buf, 1, 8))\n    header.cmd = string.byte(buf, 9)\n    header.status = string.byte(buf, 10)\n    return header\nend\n\nfunction fix_string(str, fix_length)\n    local len = string.len(str)\n    if len > fix_length then\n        len = fix_length\n    end\n    local fix_str = string.sub(str, 1, len)\n    if len < fix_length then\n        fix_str = fix_str .. string.rep(\"\\00\", fix_length - len )\n    end\n    return fix_str\nend\n\nfunction strip_string(str)\n    local pos = string.find(str, \"\\00\")\n    if pos then\n        return string.sub(str, 1, pos - 1)\n    else\n        return str\n    end\nend\n\nfunction get_ext_name(filename)\n    local extname = filename:match(\"%.(%w+)$\")\n    if extname then\n        return fix_string(extname, FDFS_FILE_EXT_NAME_MAX_LEN)\n    else\n        return nil\n    end\nend\n\nfunction read_tracket_result(sock, header)\n    if header.len > 0 then\n        local res = {}\n        local buf = sock:receive(header.len)\n        res.group_name = strip_string(string.sub(buf, 1, 16))\n        res.host       = strip_string(string.sub(buf, 17, 31)) \n        res.port       = buf2int(string.sub(buf, 32, 39))\n        res.store_path_index = string.byte(string.sub(buf, 40, 40))\n        return res\n    else\n        return nil\n    end\nend\n\nfunction read_storage_result(sock, header)\n    if header.len > 0 then\n        local res = {}\n        local buf = sock:receive(header.len)\n        res.group_name = strip_string(string.sub(buf, 1, 16))\n        res.file_name  = strip_string(string.sub(buf, 17, header.len))\n        return res\n    else\n        return nil\n    end\nend\n\nfunction query_upload_storage(self, group_name)\n    local tracker = self.tracker\n    if not tracker then\n        return nil\n    end\n    local out = {}\n    if group_name then\n        -- query upload with group_name\n        -- package length\n        table.insert(out, int2buf(16))\n        -- cmd\n        table.insert(out, string.char(TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITH_GROUP_ONE))\n        -- status\n        table.insert(out, \"\\00\")\n        -- group name\n        table.insert(out, fix_string(group_name, 16))\n    else\n        -- query upload without group_name\n        -- package length\n        table.insert(out,  string.rep(\"\\00\", FDFS_PROTO_PKG_LEN_SIZE))\n        -- cmd\n        table.insert(out, string.char(TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ONE))\n        -- status\n        table.insert(out, \"\\00\")\n    end\n    -- init socket\n    local sock, err = ngx.socket.tcp()\n    if not sock then\n        return nil, err\n    end\n    if self.timeout then\n        sock:settimeout(self.timeout)\n    end\n    -- connect tracker\n    local ok, err = sock:connect(tracker.host, tracker.port)\n    if not ok then\n        return nil, err\n    end\n    -- send request\n    local bytes, err = sock:send(out)\n    -- read request header\n    local hdr = read_fdfs_header(sock)\n    -- read body\n    local res = read_tracket_result(sock, hdr)\n    -- keepalive\n    local keepalive = self.tracker_keepalive\n    if keepalive then\n        sock:setkeepalive(keepalive.timeout, keepalive.size)\n    end\n    return res\nend\n\nfunction do_upload_appender(self, ext_name)\n    local storage = self:query_upload_storage()\n    if not storage then\n        return nil\n    end\n    -- ext_name\n    if ext_name then\n        ext_name = fix_string(ext_name, FDFS_FILE_EXT_NAME_MAX_LEN)\n    end\n    -- get file size\n    local file_size = tonumber(ngx.var.content_length)\n    if not file_size or file_size <= 0 then\n        return nil\n    end\n    local sock, err = ngx.socket.tcp()\n    if not sock then\n        return nil, err\n    end\n    if self.timeout then\n        sock:settimeout(self.timeout)\n    end\n    local ok, err = sock:connect(storage.host, storage.port)\n    if not ok then\n        return nil, err\n    end\n    -- send header\n    local out = {}\n    table.insert(out, int2buf(file_size + 15))\n    table.insert(out, string.char(STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE))\n    -- status\n    table.insert(out, \"\\00\")\n    -- store_path_index\n    table.insert(out, string.char(storage.store_path_index))\n    -- filesize\n    table.insert(out, int2buf(file_size))\n    -- exitname\n    table.insert(out, ext_name)\n    local bytes, err = sock:send(out)\n    -- send file data\n    local send_count = 0\n    local req_sock, err = ngx.req.socket()\n    if not req_sock then\n        ngx.log(ngx.ERR, err)\n        ngx.exit(500)\n    end\n        while true do\n        local chunk, _, part = req_sock:receive(1024 * 32)\n        if not part then\n            local bytes, err = sock:send(chunk)\n            if not bytes then\n                ngx.log(ngx.ngx.ERR, \"fdfs: send body error\")\n                sock:close()\n                ngx.exit(500)\n            end\n            send_count = send_count + bytes\n        else\n            -- part have data, not read full end\n            local bytes, err = sock:send(part)\n            if not bytes then\n                ngx.log(ngx.ngx.ERR, \"fdfs: send body error\")\n                sock:close()\n                ngx.exit(500)\n            end\n            send_count = send_count + bytes\n            break\n        end\n    end\n    if send_count ~= file_size then\n        -- send file not full\n        ngx.log(ngx.ngx.ERR, \"fdfs: read file body not full\")\n        sock:close()\n        ngx.exit(500)\n    end\n    -- read response\n    local res_hdr = read_fdfs_header(sock)\n    local res = read_storage_result(sock, res_hdr)\n    local keepalive = self.storage_keepalive\n    if keepalive then\n        sock:setkeepalive(keepalive.timeout, keepalive.size)\n    end\n    return res\nend\n\nfunction do_upload(self, ext_name)\n    local storage = self:query_upload_storage()\n    if not storage then\n        return nil\n    end\n    -- ext_name\n    if ext_name then\n        ext_name = fix_string(ext_name, FDFS_FILE_EXT_NAME_MAX_LEN)\n    end\n    -- get file size\n    local file_size = tonumber(ngx.var.content_length)\n    if not file_size or file_size <= 0 then\n        return nil\n    end\n    local sock, err = ngx.socket.tcp()\n    if not sock then\n        return nil, err\n    end\n    if self.timeout then\n        sock:settimeout(self.timeout)\n    end\n    local ok, err = sock:connect(storage.host, storage.port)\n    if not ok then\n        return nil, err\n    end\n    -- send header\n    local out = {}\n    table.insert(out, int2buf(file_size + 15))\n    table.insert(out, string.char(STORAGE_PROTO_CMD_UPLOAD_FILE))\n    -- status\n    table.insert(out, \"\\00\")\n    -- store_path_index\n    table.insert(out, string.char(storage.store_path_index))\n    -- filesize\n    table.insert(out, int2buf(file_size))\n    -- exitname\n    table.insert(out, ext_name)\n    local bytes, err = sock:send(out)\n    -- send file data\n    local send_count = 0\n    local req_sock, err = ngx.req.socket()\n    if not req_sock then\n        ngx.log(ngx.ERR, err)\n        ngx.exit(500)\n    end\n    while true do\n        local chunk, _, part = req_sock:receive(1024 * 32)\n        if not part then\n            local bytes, err = sock:send(chunk)\n            if not bytes then\n                ngx.log(ngx.ngx.ERR, \"fdfs: send body error\")\n                sock:close()\n                ngx.exit(500)\n            end\n            send_count = send_count + bytes\n        else\n            -- part have data, not read full end\n            local bytes, err = sock:send(part)\n            if not bytes then\n                ngx.log(ngx.ngx.ERR, \"fdfs: send body error\")\n                sock:close()\n                ngx.exit(500)\n            end\n            send_count = send_count + bytes\n            break\n        end\n    end\n    if send_count ~= file_size then\n        -- send file not full\n        ngx.log(ngx.ngx.ERR, \"fdfs: read file body not full\")\n        sock:close()\n        ngx.exit(500)\n    end\n    -- read response\n    local res_hdr = read_fdfs_header(sock)\n    local res = read_storage_result(sock, res_hdr)\n    local keepalive = self.storage_keepalive\n    if keepalive then\n        sock:setkeepalive(keepalive.timeout, keepalive.size)\n    end\n    return res\nend\n\nfunction query_update_storage_ex(self, group_name, file_name)\n    local out = {}\n    -- package length\n    table.insert(out, int2buf(16 + string.len(file_name)))\n    -- cmd\n    table.insert(out, string.char(TRACKER_PROTO_CMD_SERVICE_QUERY_UPDATE))\n    -- status\n    table.insert(out, \"\\00\")\n    -- group_name\n    table.insert(out, fix_string(group_name, 16))\n    -- file name\n    table.insert(out, file_name)\n    -- get tracker\n    local tracker = self.tracker\n    if not tracker then\n        return nil\n    end\n    -- init socket\n    local sock, err = ngx.socket.tcp()\n    if not sock then\n        return nil, err\n    end\n    if self.timeout then\n        sock:settimeout(self.timeout)\n    end\n    -- connect tracker\n    local ok, err = sock:connect(tracker.host, tracker.port)\n    if not ok then\n        return nil, err\n    end\n    -- send request\n    local bytes, err = sock:send(out)\n    -- read request header\n    local hdr = read_fdfs_header(sock)\n    -- read body\n    local res = read_tracket_result(sock, hdr)\n    -- keepalive\n    local keepalive = self.tracker_keepalive\n    if keepalive then\n        sock:setkeepalive(keepalive.timeout, keepalive.size)\n    end\n    return res\nend\n\nfunction query_update_storage(self, fileid)\n    local pos = fileid:find('/')\n    if not pos then\n        return nil\n    else\n        local group_name = fileid:sub(1, pos-1)\n        local file_name  = fileid:sub(pos + 1)\n        local res = self:query_update_storage_ex(group_name, file_name)\n        if res then\n            res.file_name = file_name\n        end\n        return res\n    end\nend\n\nfunction do_delete(self, fileid)\n    local storage = self:query_update_storage(fileid)\n    if not storage then\n        return nil\n    end\n    local out = {}\n    table.insert(out, int2buf(16 + string.len(storage.file_name)))\n    table.insert(out, string.char(STORAGE_PROTO_CMD_DELETE_FILE))\n    table.insert(out, \"\\00\")\n    -- group name\n    table.insert(out, fix_string(storage.group_name, 16))\n    -- file name\n    table.insert(out, storage.file_name)\n    -- init socket\n    local sock, err = ngx.socket.tcp()\n    if not sock then\n        return nil, err\n    end\n    sock:settimeout(self.timeout)\n    local ok, err = sock:connect(storage.host, storage.port)\n    if not ok then\n        return nil, err\n    end\n    local bytes, err = sock:send(out)\n    if not bytes then\n        ngx.log(ngx.ngx.ERR, \"fdfs: send body error\")\n        sock:close()\n        ngx.exit(500)\n    end\n    -- read request header\n    local hdr = read_fdfs_header(sock)\n    local keepalive = self.storage_keepalive\n    if keepalive then\n        sock:setkeepalive(keepalive.timeout, keepalive.size)\n    end\n    return hdr\nend\n\nfunction query_download_storage(self, fileid)\n    local pos = fileid:find('/')\n    if not pos then\n        return nil\n    else\n        local group_name = fileid:sub(1, pos-1)\n        local file_name  = fileid:sub(pos + 1)\n        local res = self:query_download_storage_ex(group_name, file_name)\n        res.file_name = file_name\n        return res\n    end\nend\n\nfunction query_download_storage_ex(self, group_name, file_name)\n    local out = {}\n    -- package length\n    table.insert(out, int2buf(16 + string.len(file_name)))\n    -- cmd\n    table.insert(out, string.char(TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ONE))\n    -- status\n    table.insert(out, \"\\00\")\n    -- group_name\n    table.insert(out, fix_string(group_name, 16))\n    -- file name\n    table.insert(out, file_name)\n    -- get tracker\n    local tracker = self.tracker\n    if not tracker then\n        return nil\n    end\n    -- init socket\n    local sock, err = ngx.socket.tcp()\n    if not sock then\n        return nil, err\n    end\n    if self.timeout then\n        sock:settimeout(self.timeout)\n    end\n    -- connect tracker\n    local ok, err = sock:connect(tracker.host, tracker.port)\n    if not ok then\n        return nil, err\n    end\n    -- send request\n    local bytes, err = sock:send(out)\n    -- read request header\n    local hdr = read_fdfs_header(sock)\n    -- read body\n    local res = read_tracket_result(sock, hdr)\n    -- keepalive\n    local keepalive = self.tracker_keepalive\n    if keepalive then\n        sock:setkeepalive(keepalive.timeout, keepalive.size)\n    end\n    return res\nend\n\nfunction do_download(self, fileid)\n    local storage = self:query_download_storage(fileid)\n    if not storage then\n        return nil\n    end\n    local out = {}\n    -- file_offset(8)  download_bytes(8)  group_name(16)  file_name(n)\n    table.insert(out, int2buf(32 + string.len(storage.file_name)))\n    table.insert(out, string.char(STORAGE_PROTO_CMD_DOWNLOAD_FILE))\n    table.insert(out, \"\\00\")\n    -- file_offset  download_bytes  8 + 8\n    table.insert(out, string.rep(\"\\00\", 16))\n    -- group name\n    table.insert(out, fix_string(storage.group_name, 16))\n    -- file name\n    table.insert(out, storage.file_name)\n    -- init socket\n    local sock, err = ngx.socket.tcp()\n    if not sock then\n        return nil, err\n    end\n    sock:settimeout(self.timeout)\n    local ok, err = sock:connect(storage.host, storage.port)\n    if not ok then\n        return nil, err\n    end\n    local bytes, err = sock:send(out)\n    if not bytes then\n        ngx.log(ngx.ERR, \"fdfs: send request error\" .. err)\n        sock:close()\n        ngx.exit(500)\n    end\n    -- read request header\n    local hdr = read_fdfs_header(sock)\n    -- read request bodya\n    local data, partial\n    if hdr.len > 0 then\n        data, err, partial = sock:receive(hdr.len)\n        if not data then\n            ngx.log(ngx.ERR, \"read file body error:\" .. err)\n            sock:close()\n            ngx.exit(500)\n        end\n    end\n    local keepalive = self.storage_keepalive\n    if keepalive then\n        sock:setkeepalive(keepalive.timeout, keepalive.size)\n    end\n    return data\nend\n\nfunction do_append(self, fileid)\n    local storage = self:query_update_storage(fileid)\n    if not storage then\n        return nil\n    end\n    local file_name = storage.file_name\n    local file_name_len = string.len(file_name)\n    -- get file size\n    local file_size = tonumber(ngx.var.content_length)\n    if not file_size or file_size <= 0 then\n        return nil\n    end\n    local sock, err = ngx.socket.tcp()\n    if not sock then\n        return nil, err\n    end\n    if self.timeout then\n        sock:settimeout(self.timeout)\n    end\n    local ok, err = sock:connect(storage.host, storage.port)\n    if not ok then\n        return nil, err\n    end\n    -- send request\n    local out = {}\n    table.insert(out, int2buf(file_size + file_name_len + 16))\n    table.insert(out, string.char(STORAGE_PROTO_CMD_APPEND_FILE))\n    -- status\n    table.insert(out, \"\\00\")\n    table.insert(out, int2buf(file_name_len))\n    table.insert(out, int2buf(file_size))\n    table.insert(out, file_name)\n    local bytes, err = sock:send(out)\n    -- send file data\n    local send_count = 0\n    local req_sock, err = ngx.req.socket()\n    if not req_sock then\n        ngx.log(ngx.ERR, err)\n        ngx.exit(500)\n    end\n    while true do\n        local chunk, _, part = req_sock:receive(1024 * 32)\n        if not part then\n            local bytes, err = sock:send(chunk)\n            if not bytes then\n                ngx.log(ngx.ngx.ERR, \"fdfs: send body error\")\n                sock:close()\n                ngx.exit(500)\n            end\n            send_count = send_count + bytes\n        else\n            -- part have data, not read full end\n            local bytes, err = sock:send(part)\n            if not bytes then\n                ngx.log(ngx.ngx.ERR, \"fdfs: send body error\")\n                sock:close()\n                ngx.exit(500)\n            end\n            send_count = send_count + bytes\n            break\n        end\n    end\n    if send_count ~= file_size then\n        -- send file not full\n        ngx.log(ngx.ngx.ERR, \"fdfs: read file body not full\")\n        sock:close()\n        ngx.exit(500)\n    end\n    -- read response\n    local res_hdr = read_fdfs_header(sock)\n    local res = read_storage_result(sock, res_hdr)\n    local keepalive = self.storage_keepalive\n    if keepalive then\n        sock:setkeepalive(keepalive.timeout, keepalive.size)\n    end\n    return res_hdr\nend\n\n-- _M.query_upload_storage = query_upload_storage\n-- _M.do_upload_storage    = do_upload_storage\n-- _M.do_delete_storage    = do_delete_storage\n\nlocal class_mt = {\n    -- to prevent use of casual module global variables\n    __newindex = function (table, key, val)\n        error('attempt to write to undeclared variable \"' .. key .. '\"')\n    end\n}\n\nsetmetatable(_M, class_mt)\n"
  },
  {
    "path": "nginx.conf",
    "content": "\n#user  nobody;\nworker_processes  1;\n\nerror_log  logs/error.log;\n#error_log  logs/error.log  notice;\n#error_log  logs/error.log  info;\n\n#pid        logs/nginx.pid;\n\n\nevents {\n    worker_connections  1024;\n}\n\n\nhttp {\n    include       mime.types;\n    default_type  application/octet-stream;\n\n    #log_format  main  '$remote_addr - $remote_user [$time_local] \"$request\" '\n    #                  '$status $body_bytes_sent \"$http_referer\" '\n    #                  '\"$http_user_agent\" \"$http_x_forwarded_for\"';\n\n    #access_log  logs/access.log  main;\n\n    sendfile        on;\n    #tcp_nopush     on;\n\n    #keepalive_timeout  0;\n    keepalive_timeout  65;\n\n    #gzip  on;\n    #\n    lua_package_path \"/usr/local/openresty/nginx/conf/lua/?.lua;;\";\n\n\n    # another virtual host using mix of IP-, name-, and port-based configuration\n    #\n    #server {\n    #    listen       8000;\n    #    listen       somename:8080;\n    #    server_name  somename  alias  another.alias;\n\n    #    location / {\n    #        root   html;\n    #        index  index.html index.htm;\n    #    }\n    #}\n\n\n    # HTTPS server\n    #\n    #server {\n    #    listen       443 ssl;\n    #    server_name  localhost;\n\n    #    ssl_certificate      cert.pem;\n    #    ssl_certificate_key  cert.key;\n\n    #    ssl_session_cache    shared:SSL:1m;\n    #    ssl_session_timeout  5m;\n\n    #    ssl_ciphers  HIGH:!aNULL:!MD5;\n    #    ssl_prefer_server_ciphers  on;\n\n    #    location / {\n    #        root   html;\n    #        index  index.html index.htm;\n    #    }\n    #}\n\n    server {\n    \tlisten 80;\n        location / {\n            default_type text/html;\n            content_by_lua '\n                ngx.say(\"<p>hello, world</p>\")\n                ';\n        }\n\n        location /hello {\n            default_type text/html;\n            echo \"hello, world\";\n        }\n\n        location /group1/M00 {\n            alias /data/images;\n\n            #set $image_root \"/usr/local/openresty/nginx/proxy_tmp/images\";\n            set $image_root \"/data/images\";\n            if ($uri ~ \"/([a-zA-Z0-9]+)/([a-zA-Z0-9]+)/([a-zA-Z0-9]+)/([a-zA-Z0-9]+)/(.*)\") {\n                set $image_dir \"$image_root/$3/$4/\";\n                set $image_name \"$5\";\n                set $file \"$image_dir$image_name\";\n            }\n\n            if (!-f $file) {\n                # 关闭lua代码缓存，方便调试lua脚本\n                #lua_code_cache off;\n                content_by_lua_file \"conf/lua/fastdfs.lua\";\n            }\n\n            #ngx_fastdfs_module;\n        }\n    }\n}\n"
  }
]