分类 默认分类 下的文章

使用golang开发联通流量机器人

背景

联通某次活动。

代码


package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "net/url"
    "os"
    "strconv"
    "strings"

    "github.com/jasonlvhit/gocron"
    flag "github.com/spf13/pflag"
)

var (
    users  = make(map[string]Login)
    c      = &http.Client{}
    logger *log.Logger

    serialNumber    string
    serialNumberKey string
)

const (
    BOSS = iota
    FRIEND
    STRANGER
    ENEMY

    //serialNumberKey    = "xxxx"
    //serialNumber       = "1860000000"
    indexUrl           = "http://worose.com:11003/GrabFlows/index.html?chl=5&serialNumber=xxxx"
    loginStepOneUrl    = "http://worose.com:11003/GrabFlows/grabflows/login!dologinpre.action"
    loginStepTwoUrl    = "http://worose.com:11003/GrabFlows/grabflows/login!getAccessKey.action"
    loginUrl           = "http://worose.com:11003/GrabFlows/grabflows/login!accessByEncodePwd.action"
    getLastRobTimeUrl  = "http://worose.com:11003/GrabFlows/grabflows/rob!getLastRobTime.action"
    getFriendsListUrl  = "http://worose.com:11003/GrabFlows/grabflows/user!getFriendList.action"
    getStrangerListUrl = "http://worose.com:11003/GrabFlows/grabflows/rob!getStrangerList.action"
    getEnemyListUrl    = "http://worose.com:11003/GrabFlows/grabflows/rob!getEnemyList.action"
    doRobUrl           = "http://worose.com:11003/GrabFlows/grabflows/rob!doRob.action"
    cookieStr          = "serialNumber=%s; currentZl=%d; currentPieceNum=%d; robPieceNum=%d; isFirstLogin=0; JSESSIONIDGrabFlows=%s"
)

type APIResponse struct {
    RetCode string `json:"retCode"`
    RetMsg  string `json:"retMsg"`
}

type RobTime struct {
    APIResponse
    RobStrangerTime      string `json:"rob_stranger_time"`
    OvRobBossCooling     string `json:"ov_rob_boss_cooling"`
    OvRobStrangerCooling string `json:"ov_rob_stranger_cooling"`
    OvRobFriendsCooling  string `json:"ov_rob_friends_cooling"`
    OvRobEnemyCooling    string `json:"ov_rob_enemy_cooling"`
    RobBossTime          string `json:"rob_boss_time"`
    RobFriendsTime       string `json:"rob_friends_time"`
    RobEnemyTime         string `json:"rob_enemy_time"`
    OvSysTime            string `json:"ov_sys_time"`
}

type Stranger struct {
    LastLandTime    string `json:"lastLandTime"`
    RobbedPieceNum  string `json:"robbedPieceNum"`
    Diff            int    `json:"diff,string"`
    SerialNumber    string `json:"serialNumber"`
    HappenTime      string `json:"happenTime"`
    CurrentZl       int    `json:"currentZl,string"`
    CurrentPieceNum string `json:"currentPieceNum"`
    FirstLandTime   string `json:"firstLandTime"`
    Att1            int    `json:"att1,string"`
}

type StrangerList struct {
    APIResponse
    RetData   string `json:"retData"`
    Strangers []Stranger
}

type Enemy struct {
    CurrentPieceNum int    `json:"CURRENT_PIECE_NUM"`
    UserId          string `json:"USER_ID"`
    SerialNumber    string `json:"SERIAL_NUMBER"`
    FirstLandTime   int    `json:"FIRST_LAND_TIME"`
    LastRobbedTime  int    `json:"LAST_ROBBED_TIME"`
    Rn              int    `json:"RN"`
    Diff            int    `json:"DIFF"`
    LastLandTime    int    `json:"LAST_LAND_TIME"`
    CurrentZl       int    `json:"CURRENT_ZL"`
    NetTypeCode     string `json:"NET_TYPE_CODE"`
    HappenTime      string `json:"HAPPENTIME"`
    RobbedPieceNum  int    `json:"ROBBED_PIECE_NUM"`
    RobbedSendSms   int    `json:"ROBBED_SEND_SMS"`
    Att1            int    `json:"ATT1,string"`
}

type EnemyList struct {
    APIResponse
    RetData   string `json:"retData"`
    TotalPage int    `json:"totalPage,string"`
    Enemys    []Enemy
}

type Friend struct {
    SerialNumber       string `json:"SERIAL_NUMBER"`
    MMdiff             int    `json:"MMDIFF"`
    FirstShipTime      int    `json:"FIRST_FRIENDSHIP_TIME"`
    FriendSerialNumber string `json:"FRIEND_SERIAL_NUMBER"`
    LastLandTime       int    `json:"LAST_LAND_TIME"`
    CurrentZl          int    `json:"CURRENT_ZL"`
    Diff               int    `json:"DIFF"`
    Rn                 int    `json:"RN"`
    HappenTime         int    `json:"HAPPENTIME"`
    UserId             string `json:"USERID"`
    Att1               int    `json:"ATT1"`
}

type FriendList struct {
    APIResponse
    TotalCount int    `json:"totalCount,string"`
    TotalPage  int    `json:"tatalPage,string"`
    RetData    string `json:"retData"`
    Friends    []Friend
}

type AccessKey struct {
    APIResponse
    RetSeq string `json:"retSeq"`
    RetDwp string `json:"retdwp"`
}

type Login struct {
    APIResponse
    IsFirstLogin    int `json:"isFirstLogin,string"`
    RobPieceNum     int `json:"robPieceNum,string"`
    CurrentPieceNum int `json:"currentPieceNum,string"`
    CurrentZl       int `json:"currentZl,string"`
    SessionId       string
}

func task() {
    logger.Println("task runing.............")
    rb, err := getLastRobTime()
    if err != nil {
        logger.Println(err)
        return
    }
    if rb.OvRobBossCooling == "0" {
        go doBoss()
    }
    if rb.OvRobFriendsCooling == "0" {
        go doFriend()
    }
    if rb.OvRobStrangerCooling == "0" {
        go doStranger()
    }
    if rb.OvRobEnemyCooling == "0" {
        go doEnemy()
    }
}

func request(serialNumber, url string, values url.Values) ([]byte, error) {
    login := users[serialNumber]
    cookie := fmt.Sprintf(cookieStr, serialNumber, login.CurrentZl, login.CurrentPieceNum, login.RobPieceNum, login.SessionId)
    logger.Println(values.Encode())
    req, _ := http.NewRequest("POST", url, strings.NewReader(values.Encode()))
    req.Header.Add("Accept", "application/json;charset=utf-8, text/javascript, */*; q=0.01")
    req.Header.Add("Accept-Encoding", "gzip, deflate")
    req.Header.Add("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.6")
    req.Header.Add("Connection", "keep-alive")
    req.Header.Add("Host", "worose.com:11003")
    req.Header.Add("Origin", "http://worose.com:11003")
    req.Header.Add("Referer", "http://worose.com:11003/GrabFlows/game.html")
    req.Header.Add("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8")
    req.Header.Add("User-Agent", "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36")
    req.Header.Add("X-Requested-With", "XMLHttpRequest")
    req.Header.Add("Cookie", cookie)
    resp, err := c.Do(req)
    if err != nil {
        logger.Println(err)
        return nil, err
    }
    defer resp.Body.Close()
    b, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        logger.Println(err)
        return nil, err
    }
    return b, err
}

func login() error {
    resp, err := http.Get(loginStepOneUrl)
    if err != nil {
        logger.Println(err)
        return err
    }
    b, _ := ioutil.ReadAll(resp.Body)
    logger.Println(string(b))
    logger.Println(resp.Cookies())
    defer resp.Body.Close()

    values := url.Values{}
    values.Add("primryKey", serialNumberKey)
    resp1, err := http.PostForm(loginStepTwoUrl, values)
    defer resp1.Body.Close()
    var ak AccessKey
    b, _ = ioutil.ReadAll(resp1.Body)
    json.Unmarshal(b, &ak)
    logger.Println(string(b))
    logger.Println(ak)
    logger.Println(resp1.Cookies())

    values = url.Values{}
    values.Add("seqid", ak.RetSeq)
    values.Add("serialNumber", serialNumber)
    values.Add("encodePwd", ak.RetDwp)
    values.Add("channelId", "5")
    res, err := http.PostForm(loginUrl, values)
    defer res.Body.Close()
    b, _ = ioutil.ReadAll(res.Body)
    logger.Println(string(b))
    logger.Println(res.Cookies())
    var login Login
    json.Unmarshal(b, &login)
    for _, v := range res.Cookies() {
        if v.Name == "JSESSIONIDGrabFlows" {
            login.SessionId = v.Value
            break
        }
    }
    users[serialNumber] = login
    return nil
}

func getLastRobTime() (rt RobTime, err error) {
    values := url.Values{}
    values.Add("serialNumber", serialNumber)
    b, err := request(serialNumber, getLastRobTimeUrl, values)
    if err != nil {
        return RobTime{}, err
    }
    var robTime RobTime
    json.Unmarshal(b, &robTime)
    return robTime, nil
}

func getStrangerList() (s StrangerList, err error) {
    values := url.Values{}
    values.Add("serialNumber", serialNumber)
    resp, err := http.PostForm(getStrangerListUrl, values)
    if err != nil {
        logger.Println(err)
        return StrangerList{}, err
    }
    defer resp.Body.Close()
    b, err := ioutil.ReadAll(resp.Body)
    var sl StrangerList
    err = json.Unmarshal(b, &sl)
    if err != nil {
        logger.Println(err)
        return StrangerList{}, err
    }
    json.Unmarshal([]byte(sl.RetData), &sl.Strangers)
    return sl, nil
}

func getEnemyList() (e EnemyList, err error) {
    values := url.Values{}
    values.Add("serialNumber", serialNumber)
    values.Add("currentPage", "1")
    values.Add("pageSize", "5")
    resp, err := http.PostForm(getEnemyListUrl, values)
    if err != nil {
        return EnemyList{}, err
    }
    defer resp.Body.Close()
    b, err := ioutil.ReadAll(resp.Body)
    var el EnemyList
    err = json.Unmarshal(b, &el)
    if err != nil {
        logger.Println(err)
        return EnemyList{}, err
    }
    json.Unmarshal([]byte(el.RetData), &el.Enemys)
    return el, nil
}

func getFriendsList() (f FriendList, err error) {
    values := url.Values{}
    values.Add("serialNumber", serialNumber)
    values.Add("currentPage", "1")
    values.Add("pageSize", "5")
    resp, err := http.PostForm(getFriendsListUrl, values)
    if err != nil {
        logger.Println(err)
        return FriendList{}, err
    }
    defer resp.Body.Close()
    b, err := ioutil.ReadAll(resp.Body)
    var fl FriendList
    err = json.Unmarshal(b, &fl)
    if err != nil {
        logger.Println(err)
        return FriendList{}, err
    }
    json.Unmarshal([]byte(fl.RetData), &fl.Friends)
    return fl, nil
}

func doRob(serialNumber string, robType int, robSerialNumber string, currentZl, robZl int) error {
    values := url.Values{}
    values.Add("serialNumber", serialNumber)
    values.Add("robType", strconv.Itoa(robType))
    values.Add("robSerialNumber", robSerialNumber)
    if currentZl > 0 {
        values.Add("currentZl", strconv.Itoa(currentZl))
        values.Add("robZl", strconv.Itoa(robZl))
    }
    b, err := request(serialNumber, doRobUrl, values)
    if err != nil {
        logger.Println(err)
        return err
    }
    logger.Println(string(b))
    return err
}

func doStranger() error {
    logger.Println("开始处理陌生人........")
    user := users[serialNumber]
    //获取陌生人列表
    sl, err := getStrangerList()
    if err != nil {
        logger.Println(err)
        return err
    }
    logger.Println(sl)
    var strangerList []Stranger
    for i := 0; i < len(sl.Strangers); i++ {
        if len(strangerList) == 2 {
            break
        }
        robZl := sl.Strangers[i].CurrentZl + sl.Strangers[i].Att1*100
        if sl.Strangers[i].Diff < 0 && robZl < user.CurrentZl {
            strangerList = append(strangerList, sl.Strangers[i])
        }
    }
    logger.Println(strangerList)
    for i := 0; i < len(strangerList); i++ {
        doRob(serialNumber, STRANGER, strangerList[i].SerialNumber, user.CurrentZl, strangerList[i].CurrentZl+strangerList[i].Att1*100)
    }
    return nil
}

func doEnemy() error {
    logger.Println("开始处理敌人........")
    user := users[serialNumber]
    //获取敌人列表
    el, err := getEnemyList()
    if err != nil {
        logger.Println(err)
        return err
    }
    logger.Println(el)
    var enemyList []Enemy
    for i := 0; i < len(el.Enemys); i++ {
        if len(enemyList) == 2 {
            break
        }
        robZl := el.Enemys[i].CurrentZl + el.Enemys[i].Att1*100
        if el.Enemys[i].Diff < 0 && robZl < user.CurrentZl {
            enemyList = append(enemyList, el.Enemys[i])
        }
    }
    logger.Println(enemyList)
    for i := 0; i < len(enemyList); i++ {
        doRob(serialNumber, ENEMY, enemyList[i].SerialNumber, user.CurrentZl, enemyList[i].CurrentZl+enemyList[i].Att1*100)
    }
    return nil
}

func doBoss() {
    logger.Println("处理BOSS...........")
    doRob(serialNumber, BOSS, "BOSS", 0, 0)
}

func doFriend() error {
    logger.Println("开始处理朋友........")
    user := users[serialNumber]
    //获取敌人列表
    el, err := getFriendsList()
    if err != nil {
        logger.Println(err)
        return err
    }
    logger.Println(el)
    var friendsList []Friend
    for i := 0; i < len(el.Friends); i++ {
        if len(friendsList) == 2 {
            break
        }
        robZl := el.Friends[i].CurrentZl + el.Friends[i].Att1*100
        if el.Friends[i].Diff < 0 && robZl < user.CurrentZl {
            friendsList = append(friendsList, el.Friends[i])
        }
    }
    logger.Println(friendsList)
    for i := 0; i < len(friendsList); i++ {
        doRob(serialNumber, FRIEND, friendsList[i].FriendSerialNumber, user.CurrentZl, friendsList[i].CurrentZl+friendsList[i].Att1*100)
    }
    return nil
}

func main() {
    flag.StringVarP(&serialNumber, "serialNumber", "s", "00000000000", "-s serialNumber")
    flag.StringVarP(&serialNumberKey, "serialNumberKey", "k", "0000000000", "-k serialNumberKey")
    flag.Parse()

    if serialNumber == "00000000000" || serialNumberKey == "00000000000" {
        fmt.Println("Usage:worose -s serialNumber -k serialNumberKey")
        os.Exit(1)
    }

    logger = log.New(os.Stderr, "worose: ", log.LstdFlags|log.Lmicroseconds|log.Lshortfile)

    logger.Println("worose running.......")
    logger.Println(serialNumber, serialNumberKey)

    login()

    task()

    gocron.Every(5).Minutes().Do(task)
    <-gocron.Start()
}


搭建图片服务(三)

为图片服务增加jwt认证

安装依赖

  • lua-resty-jwt-0.1.11.tar.gz
  1. 解压
   tar -zxf lua-resty-jwt-0.1.11.tar.gz

  1. 安装
   cd cd lua-resty-jwt-0.1.11/lib/
   cp -R resty /usr/local/luajit/lib/lua
  • basexx
  1. 下载
  https://github.com/aiq/basexx/blob/master/lib/basexx.lua
  1. 安装
cp basexx.lua /usr/local/nginx/lua_lib

  • nginx-jwt.lua
  1. 下载
   https://github.com/auth0/nginx-jwt/blob/master/nginx-jwt.lua
  1. 安装
   cp nginx-jwt.lua /usr/local/nginx/lua_lib/
  1. 修改
   47行 local jwt_obj = jwt:verify(secret, token, 0)
   修改为 local jwt_obj = jwt:verify(secret, token)

配置环境变量

   export JWT_SECRET=4Tz7JuEERJkrIsU=
   export JWT_SECRET_IS_BASE64_ENCODED=true

配置nginx.conf

   location /upload {
        access_by_lua '
            local jwt = require "nginx-jwt"
            jwt.auth()
        ';
        error_log logs/upload_err.log;
        content_by_lua_file lua_lib/nginx_upload.lua;
   } 

完。

搭建图片服务(二)

图片写入Mongodb

所需软件

  • lua-resty-moongoo-master.zip
  • lua-cbson-master.zip
  • mongo-c-driver-1.7.0.tar.gz

依赖安装

  • 安装mongo-c-driver
  1. 解压mongo-c-driver-1.7.0.tar.gz
    tar -zxf mongo-c-driver-1.7.0.tar.gz
  2. 安装依赖
    yum install cyrus-sasl-devel
  3. 编译安装
    ./configure --enable-sasl=yes
    make && make install
  4. 链接库
    ln -s /usr/local/lib/libbson-1.0.so.0 /lib64/libbson-1.0.so.0
  • 安装lua-cbson
  1. 解压lua-cbson-master.zip

    unzip lua-cbson-master.zip

  2. 安装依赖

    yum install cmake gcc gcc-c++

  3. 编辑FindLuaJIT.cmake文件

   cd cmake
   vi FindLuaJIT.cmake
   在find_path和find_library加入/usr/local/luajit/ 
  1. 配置
    mkdir build && cd build && cmake ..

  2. 编译安装
    make && make install

  • 安装lua-resty-moongoo
  1. 解压lua-resty-moongoo-master.zip
    unzip lua-resty-moongoo-master.zip
  2. 安装
    cd lib && cp -R resty /usr/local/luajit/lib/lua/

上传脚本

  • mongo_write.lua
  • nginx_upload_for_mongodb.lua

修改nginx.conf

location /upload_to_mongodb {
    error_log logs/upload_err.log;
    content_by_lua_file lua_lib/nginx_upload_for_mongodb.lua;
}

重新载入

nginx -s reload

脚本内容

  • mongo_write.lua
local moongoo = require("resty.moongoo")
local cbson = require("cbson")

local _M = {}           -- 局部的变量
_M._VERSION = '1.0'     -- 模块版本
local mt = {__index = _M}

function _M:new(o,url,dbname)
    o = o or {}
    self.__index = self
    self.url = url
    self.dbname = dbname
    self.mg = nil
    setmetatable(o,self)
    return o
end

function _M:connection()
   local mg, err = moongoo.new(self.url)
   if not mg then
    error(err)
   end
   return mg
end

function _M:writeFile(filename,data,lng,lat)
   if self.mg == nil then
     self.mg = self:connection(self.url)
   end
   local gridfs = self.mg:db(self.dbname):gridfs()
   local fs = gridfs:create(filename,{filename=filename,date=os.date(),metadata={longitude=lng,latitude=lat}},false)
   fs:write(data)
   fs:close()
end

function _M:updateInfo(fns,metadata)
   if self.mg == nil then
     self.mg = self:connection(self.url)
   end
   local c = self.mg:db(self.dbname):collection("fs.files")
   for _,fn in ipairs(fns) do
      local fsFile = c:find_one({filename=fn})
      fsFile['metadata']= metadata
      c:update({filename=fn},fsFile)
   end
end

function _M:close()
   if self.mg ~= nil then
      self.mg:close()
   end
end

return _M

  • nginx_upload_for_mongodb.lua
string.split = function(line, sep)

    sep = sep or ' '
    local retval = {}
    local pos = 1
    while true do
        local from, to = string.find(line, sep, pos)
        if from then
            local item = string.sub(line, pos, from-1)
            table.insert( retval, item )
            pos = to + 1
        else
            local item = string.sub(line, pos)
            table.insert( retval, item )
            break
        end
    end
    return retval
end    

string.trim = function (s)
  return (s:gsub("^%s*(.-)%s*$", "%1"))
end

--获取扩展名  
function getExtension(filename)  
    return filename:match(".+%.(%w+)$")  
end

local stringy = require "stringy"
local resty_sha1 = require "resty.sha1"
local upload = require "resty.upload"
local cjson = require "cjson"
local chunk_size = 4096
local form = upload:new(chunk_size)
local sha1 = resty_sha1:new()
local file
local longitude='11111'
local latitude='22222'
local extnames={"png","jpg","jpeg","gif"}
local msg = {success=false,msg='上传失败',data=nil}


local mongo = require("mongo_write")
local mg = mongo:new(nil,"mongodb://user:password@ip:port/dbname?authMechanism=SCRAM-SHA-1",'dbname')

function extExists(ext)
    for _, value in pairs(extnames) do
        if value == ext then
            return true
        end
    end

    return false
end

local fileNames={}
local fileRes={}
local part_name, part_value
local params={}
local ids={}
while true do
    local typ, res, err = form:read()

    if not typ then
        ngx.say("failed to read: ", err)
        return
    end

    if typ == "header" then
        local key=res[1]
        local value=res[2]
        
        if stringy.startswith(string.lower(res[1]), "content-disposition") then
                local parts = stringy.split(res[3], ";")
                local current_parts = stringy.split(stringy.strip(parts[2]), "=")
                if string.lower(table.remove(current_parts, 1)) == "name" then
                    local current_value = stringy.strip(table.remove(current_parts, 1))
                    part_name = string.sub(current_value, 2, string.len(current_value) - 1)
                end
        end

        if key =="Content-Disposition" then
             local kvlist=string.split(value,';')

             for _, kv in ipairs(kvlist) do
                    local seg = string.trim(kv)
                    --ngx.log(ngx.ERR,seg)
                    if seg:find("filename") then
                        local kvfile = string.split(seg, "=")
                        filename = string.sub(kvfile[2], 2, -2)
                        if filename then
                            --获取文件后缀名字
                            fileExtension=getExtension(filename)
                            --ngx.log(ngx.ERR,fileExtension)
                            if extExists(fileExtension) == false then
                               msg['msg'] = '文件类型错误'
                               ngx.say(cjson.encode(msg))
                               return 1
                            end
                            local linuxTime=tostring(os.time())
                            local filename = linuxTime..filename
                            filePath= "/tmp/attachment/image/"..filename
                            file,errmsg = io.open(filePath, "w+")
                            --存储的文件路径                    
                            --ngx.say("failed to open file ", filePath)
                            if not file then
                                ngx.say("failed to open file ", filePath .. errmsg)
                                return 1
                            end
                            table.insert(fileNames,filename)
                        else
                            return 1
                        end
                        --跳出循环
                        break 
                    end
             end
        end
    elseif typ == "body" then
        if part_name ~= nil and part_name ~="" and part_name ~='file' then
                part_value = res
        end
        if file then
                table.insert(fileRes,res)
                file:write(res)
                sha1:update(res)
        end

    elseif typ == "part_end" then
        if part_name ~= nil and part_name ~="" and part_name ~='file' then
           params[part_name] = part_value

           -- Reset fields for the next part
           part_value = nil
           part_name = nil
        end

        if file then
           local filename = fileNames[#fileNames]
           --ngx.log(ngx.ERR,filename)   
           mg:writeFile(filename,table.concat(fileRes),longitude,latitude)
           fileRes = nil
           file:close()
           file = nil
        end
        local sha1_sum = sha1:final()
        sha1:reset()
        --my_save_sha1_sum(sha1_sum)
    elseif typ == "eof" then
        msg['success'] = true
        msg['msg'] = '上传成功'
        msg['data']= fileNames

        mg:updateInfo(fileNames,params)
                mg:close()
        ngx.say(cjson.encode(msg))
        fileNames = nil
        break

    else
        -- do nothing
    end
end

搭建图片服务(一)

软件列表

  • nginx-1.12.0.tar.gz
  • LuaJIT-2.0.5.zip
  • lua-nginx-module-0.10.10.zip
  • ngx_devel_kit-0.3.0.zip
  • lua-resty-upload-0.10.zip
  • lua-resty-string-0.10.zip
  • lua-cjson-2.1.0.tar.gz
  • luarocks-2.4.3.tar.gz
  • lua-stringy-0.4-1.zip
  • gd-devel-2.0.35-11.el6.x86_64.rpm (centos 6.x)
  • gd-devel-2.0.35-26.el7.x86_64.rpm (centos 7.x)

安装步骤

安装luajit

  1. 解压 unzip LuaJIT-2.0.5.zip
  2. 修改Makefile export PREFIX= /usr/local/luajit
  3. 编译安装 make && make install
  4. 配置环境变量
   vi /etc/profile
   export LUAJIT_LIB=/usr/local/luajit/lib
   export LUAJIT_INC=/usr/local/luajit/include/luajit-2.0 
   source /etc/profile

nginx安装

  1. 创建用户
useradd -M -s /sbin/nologin nginx

  1. 安装依赖
yum install gcc pcre pcre-devel zlib zlib-devel openssl openssl-devel libxml2 libxml2-devel libxslt libxslt-devel gd-devel geoip geoip-devel -y

yum install gd fontconfig-devel freetype-devel libX11-devel libXpm-devel libjpeg-devel libpng-devel -y

rpm -ivh gd-devel-2.0.35-11.el6.x86_64.rpm

  1. 编译安装
./configure \
 --prefix=/usr/local/nginx \
 --user=nginx \
 --group=nginx \
 --with-pcre \
 --with-http_ssl_module \
 --with-http_v2_module \
 --with-http_realip_module \
 --with-http_addition_module \
 --with-http_sub_module \
 --with-http_dav_module \
 --with-http_flv_module \
 --with-http_mp4_module \
 --with-http_gunzip_module \
 --with-http_gzip_static_module \
 --with-http_random_index_module \
 --with-http_secure_link_module \
 --with-http_stub_status_module \
 --with-http_auth_request_module \
 --with-http_image_filter_module \
 --with-mail \
 --with-mail_ssl_module \
 --with-stream_ssl_module \
 --add-module=/root/ngx_devel_kit-0.3.0 \
 --add-module=/root/lua-nginx-module-0.10.10

 make && make install

  1. 启动测试

./nginx: error while loading shared libraries: libluajit-5.1.so.2: cannot open shared object file: No such file or directory

执行 ln -s /usr/local/lib/libluajit-5.1.so.2 /lib64/libluajit-5.1.so.2

配置lua

  1. 修改nginx.conf

server前面增加

 lua_package_path "/usr/local/luajit/lib/lua/?.lua;/usr/local/nginx/lua_lib/?.lua;;";
 lua_package_cpath "/usr/local/luajit/lib/lua/5.1/?.so;;";

http里面增加

location /hello {
    default_type text/html;
    content_by_lua_file lua_lib/hello.lua
}

创建hello.lua

mkdir /usr/local/nginx/lua_lib/
cd /usr/local/nginx/lua_lib/
touch hello.lua
vi hello.lua

ngx.say('hello lua!')

测试访问 http://127.0.0.1/hello

配置图片上传

  1. 安装lua-cjson-2.1.0.tar.gz
  • tar -zxf lua-cjson-2.1.0.tar.gz
  • cd lua-cjson-2.1.0
  • 修改Makefile PREFIX = /usr/local/luajit
  • 修改Makefile LUA_INCLUDE_DIR = $(PREFIX)/include/luajit-2.0
  • make && make install
  1. 安装lua-resty-string-0.10.zip
  • unzip lua-resty-string-0.10.zip
  • cd lua-resty-string-0.10
  • 修改Makefile PREFIX ?= /usr/local/luajit
  • 修改Makefile LUA_INCLUDE_DIR ?= $(PREFIX)/include/luajit-2.0
  • make install
  1. 安装lua-resty-upload-0.10.zip
  • unzip lua-resty-upload-0.10.zip
  • cd lua-resty-upload-0.10
  • 修改Makefile PREFIX ?= /usr/local/luajit
  • 修改Makefile LUA_INCLUDE_DIR ?= $(PREFIX)/include/luajit-2.0
  • make install

4.安装luarocks

  • tar -zxf luarocks-2.4.3.tar.gz
  • cd luarocks-2.4.3
  • ./configure --with-lua-include=/usr/local/luajit/include/luajit-2.0 --prefix=/usr/local/luajit
  • make && make install
  • 将/usr/local/luajit/bin加入path
  1. 安装lua-stringy-0.4-1.zip
  • unzip lua-stringy-0.4-1.zip
  • cd lua-stringy-0.4-1
  • ln -s /usr/local/luajit/share/lua/5.1/luarocks /usr/share/lua/5.1/luarocks
  • luarocks make stringy/stringy-0.4-1.rockspec

上传nginx_upload.lua脚本

  1. 脚本内容
string.split = function(line, sep)

    sep = sep or ' '
    local retval = {}
    local pos = 1
    while true do
        local from, to = string.find(line, sep, pos)
        if from then
            local item = string.sub(line, pos, from-1)
            table.insert( retval, item )
            pos = to + 1
        else
            local item = string.sub(line, pos)
            table.insert( retval, item )
            break
        end
    end
    return retval
end    

string.trim = function (s)
  return (s:gsub("^%s*(.-)%s*$", "%1"))
end

--获取扩展名  
function getExtension(filename)  
    return filename:match(".+%.(%w+)$")  
end

local stringy = require "stringy"
local resty_sha1 = require "resty.sha1"
local upload = require "resty.upload"
local cjson = require "cjson"
local chunk_size = 4096
local form = upload:new(chunk_size)
local sha1 = resty_sha1:new()
local file
local extnames={"png","jpg","jpeg","gif"}
local msg = {success=false,msg='上传失败',data=nil}


function extExists(ext)
    for _, value in pairs(extnames) do
        if value == ext then
            return true
        end
    end

    return false
end

local fileNames={}
local fileRes={}
local part_name, part_value
local params={}
while true do
    local typ, res, err = form:read()

    if not typ then
        ngx.say("failed to read: ", err)
        return
    end

    if typ == "header" then
        local key=res[1]
        local value=res[2]
        
        if stringy.startswith(string.lower(res[1]), "content-disposition") then
                local parts = stringy.split(res[3], ";")
                local current_parts = stringy.split(stringy.strip(parts[2]), "=")
                if string.lower(table.remove(current_parts, 1)) == "name" then
                    local current_value = stringy.strip(table.remove(current_parts, 1))
                    part_name = string.sub(current_value, 2, string.len(current_value) - 1)
                end
        end

        if key =="Content-Disposition" then
             local kvlist=string.split(value,';')

             for _, kv in ipairs(kvlist) do
                    local seg = string.trim(kv)
                    --ngx.log(ngx.ERR,seg)
                    if seg:find("filename") then
                        local kvfile = string.split(seg, "=")
                        filename = string.sub(kvfile[2], 2, -2)
                        if filename then
                            --获取文件后缀名字
                            fileExtension=getExtension(filename)
                            --ngx.log(ngx.ERR,fileExtension)
                            if extExists(fileExtension) == false then
                               msg['msg'] = '文件类型错误'
                               ngx.say(cjson.encode(msg))
                               return 1
                            end
                            local linuxTime=tostring(os.time())
                            local filename = linuxTime..filename
                            filePath= "/tmp/attachment/image/"..filename
                            file,errmsg = io.open(filePath, "w+")
                            --存储的文件路径                    
                            --ngx.say("failed to open file ", filePath)
                            if not file then
                                ngx.say("failed to open file ", filePath .. errmsg)
                                return 1
                            end
                            table.insert(fileNames,filename)
                        else
                            return 1
                        end
                        --跳出循环
                        break 
                    end
             end
        end
    elseif typ == "body" then
        if part_name ~= nil and part_name ~="" and part_name ~='file' then
          part_value = res
        end
        if file then
                table.insert(fileRes,res)
                file:write(res)
                sha1:update(res)
        end

    elseif typ == "part_end" then
        if part_name ~= nil and part_name ~="" and part_name ~='file' then
           params[part_name] = part_value

           -- Reset fields for the next part
           part_value = nil
           part_name = nil
        end

        if file then
           local filename = fileNames[#fileNames]
           --ngx.log(ngx.ERR,filename)   
           fileRes = nil
           file:close()
           file = nil
        end
        local sha1_sum = sha1:final()
        sha1:reset()
        --my_save_sha1_sum(sha1_sum)
    elseif typ == "eof" then
        msg['success'] = true
        msg['msg'] = '上传成功'
        msg['data']= fileNames

        ngx.say(cjson.encode(msg))
        fileNames = nil
        break

    else
        -- do nothing
    end
end

  1. 创建图片目录
mkdir -p /tmp/attachment/image
chmod -R 777 /tmp/attachment/image

  1. 配置nginx.conf
location /upload {
    error_log logs/upload_err.log;
    content_by_lua_file lua_lib/nginx_upload.lua;
}

  1. 使用POSTMAN测试

配置裁剪缩略图

  1. 安装依赖
yum install ImageMagick

  1. 上传imagemagick.lua脚本
-- http://domain.com/111/photo/201411/05/5459e306820af926411357_320x320.jpg
-- config
local image_sizes = {"640x640", "320x320", "124x124", "140x140", "64x64", "60x60", "32x32", "0x0"}


-- parse uri
function parseUri(uri)
    local _, _, name, ext, size = string.find(uri, "(.+)(%..+)!(%d+x%d+)")
    --ngx.header.content_type = "text/plain";
    --ngx.say(name,size);
    if name and size and ext then
        return ngx.var.image_root .. name .. ext, size
    else
        return "", ""
    end
end

function fileExists(name)
    local f = io.open(name, "r")
    if f ~= nil then
        io.close(f)
        return true
    else
        return false
    end
end
--ngx.header.content_type = "text/plain";
--ngx.say(name);
function sizeExists(size)
    for _, value in pairs(image_sizes) do
        if value == size then
            return true
        end
    end
    
    return false
end
--ngx.header.content_type = "text/plain";
--ngx.say(size);
function resize()
    local ori_filename, size = parseUri(ngx.var.uri)
    --ngx.header.content_type = "text/plain";
    ngx.log(ngx.ERR,ori_filename..'!'..size);
    if fileExists(ori_filename) == false then  --or sizeExists(size) == false then
        ngx.exit(404)
    end
    --local i = string.find(ori_filename,'.',1,true)
    --local name = string.sub(ori_filename,0,i-1)
    --local ext = string.sub(ori_filename,i,string.len(ori_filename))
    --local dist_file_name = name..size..ext
    --ngx.log(ngx.ERR,'-----'..dist_file_name..'------------') 
    local command = '';
    if size == '0x0' then
        command = table.concat({
            ngx.var.convert_bin,
            ori_filename,
            "/usr/local/nginx/conf/logo.png",
            "-gravity southeast -geometry +5+10 -composite",
            ngx.var.file,
        }, " ")
    else
        command = table.concat({
            ngx.var.convert_bin,
            ori_filename,
            "/usr/local/nginx/conf/logo.png",
            "-gravity southeast -geometry +5+10 -composite -resize",
            size,
            ngx.var.file,
        }, " ")
    end
    --ngx.header.content_type = "text/plain";
    --ngx.say(command);
    os.execute(command)
end
--ngx.header.content_type = "text/plain";
--ngx.say(command);
resize()

  1. 配置nginx.conf
rewrite ^/attachment/image/(.*)\.(png|jpg|jpeg|gif)$ /attachment/image/$1.$2!0x0 last;
location /attachment/image/ {
    root /tmp;
    set $image_root "/tmp";
    set $file "$image_root$uri";
    set $convert_bin "/usr/bin/convert";
    if (!-f $file){
       rewrite_by_lua_file lua_lib/imagemagick.lua;
    }
    expires max;
}
  1. 上传水印图片 /usr/local/nginx/conf/logo.png

  2. 重新加载配置 nginx -s reload

  3. 测试

    http://127.0.0.1/attachment/image/aaaa.png!128x128