Lua的文件操作


先簡單介紹一下被迫使用Lua的IO的情境:

游戲支持玩家自定義上傳頭像,在排行榜中會顯示玩家列表(包括本服、跨服),原有的做法是先檢測CCUserDefault中是否存在指定圖片的key以及它的狀態。然后在下載頭像、下載完成后設置對應的狀態。這樣導致的一個問題就是CCUserDefault的讀寫完全失效了。整個游戲下載的補丁包判斷和其它判斷就完全失效了,不得卸載游戲后重裝。個人目前的推測是由於多線程引起的,暫時沒有有效的依據

下載頭像使用的是libcurl,嗯,又是它,在做項目這么久的過程中,發現它其實有很多地方比較坑。其中有一點我一直沒搞明白,同樣的一樣地址,系統自帶的瀏覽器(IOS、Android均支持)就能正常返回,而游戲中使用libcurl去下載就是死活返回errcode 28 (CURLE_OPERATION_TIMEDOUT),libcurl我設置的是60秒超時,絕對足夠了

 

之后我做了優化,在設置自定義頭像的時候,先檢測本地是否有該文件,如果有直接就設置了,如果沒有就放置在加載隊列中,等下載完成后再設置頭像,只開一條線程去下載圖片。(同一張頭像的url只請求一次,也避免對CCUserDefault的讀寫操作)。

 

通過libcurl下載一個“頭像id.jpg.partial”的文件,然后下載完成重新寫一個“頭像id.jpg”的文件。在下載完成的時候,只做了簡單的一個文件大小判斷,如果文件小於300B就認為它是有問題的,直接刪除相應的文件

-- filePath為當前下載完成的臨時頭像文件路徑
local targetIconUrl = string.gsub(filePath, ".partial", "")
 
local inpFile = io.open(filePath, "rb")
local outFile = io.open(targetIconUrl, "wb")
 
if inpFile ~= nil then
    -- 最大8KB的內存
    local buffSize = 2^13
    while true do 
        local bytes = inpFile:read(buffSize)
        if not bytes then
            break
        end

        outFile:write(bytes)
    end

    inpFile:close()
end

-- 獲取下載icon的大小
if outFile ~= nil then
    local current = outFile:seek()
    local fileSize = outFile:seek("end")
    outFile:seek("set", current)

    cclog("==> targetIconUrl : "..tostring(targetIconUrl)..", fileSize : "..tostring(fileSize))

    outFile:close()

    -- 小於300字節均認為不正常的數據
    if fileSize < 300 then
        FileUtil:DeleteFile_(filePath)
        FileUtil:DeleteFile_(targetIconUrl)

        self:DownloadNextIconHandler()
        do return end
    end
end

本來,直接調用對應的FileUtil中的FileRename方法就可以實現文件的重命名,但是線上的版本沒有導出相應的方法,導致目前只能通過Lua的IO來實現。

最近再看lua的源碼時,才真正意識到luaconf.h中定義的 LUAI_MAXCSTACK 是 cclosure的upvalue上限,而lua內存上限似乎沒有找到明確的代碼。

而file:read調用的是liolib.c

 

底層通過調用fread方法來獲得文件的內容,默認每次最多讀取512(LUAL_BUFFERSIZE的值)

 

然后調用file:seek(“end”)來獲取文件大小

底層調用feek方法來實現

 

本以為到這里就結束了,實際上我遇到另外一個問題。如果頭像因審核問題被刪除了,導致404,結果底層libcurl方法沒有判斷http status code,直接判斷CURLcode的值是否為CURLE_OK,導致將得到的文件直接寫入了。但我從崩潰的日志上得到的信息是,小米4這台設備上獲得的文件大小為18378

之后就直接報

invalid address or address of corrupt block 0x7c0eaa40 passed to dlfree

 

之后我修改了libcurl下載文件的代碼,但要等下次打整包的時候才能用上

 

把不是jpeg的圖片直接對CCSprite進行路徑賦值的時候就over了,所以需要一個檢測文件是否為jpeg的方法

-- 判斷資源是否為jpg
function PCUtils:CheckIsJpeg(filePath)
    local isJpeg = false

    if FileUtil:CheckFileExistWithFullPath(filePath) then
        local inpFile = io.open(filePath, "rb")
        -- 讀取前三位
        local bytes = inpFile:read(3)
        if bytes then
            local fileHeadIden = ""
            for _, b in ipairs{string.byte(bytes, 1, -1)} do
                local val = string.format("%02X", b)
                fileHeadIden = fileHeadIden..val
            end

            if string.upper(fileHeadIden) == "FFD8FF" then
                isJpeg = true
            else
                cclog("==> filePath : "..tostring(filePath)..", fileHeadIden : "..tostring(fileHeadIden))
            end
        end

        inpFile:close()
    end

    return isJpeg
end

讀取文件的前三位,轉換為16進制,然后對比JPEG的頭部,判斷是否為JPEG格式的文件,這個是我想起自己之前寫過的一篇文章《node.js獲取圖片文件的真實類型

 

文件一些方法和代碼,比如為何是r + b,以及2^13(8KB內存)這種技巧,都是參考《Lua程序設計 第二版》第21章  I/O庫,網上應該有中文版的PDF下載,自行搜索吧…

 

本文參考:

lua cclosure 的 upvalue 數量限制

Lua編碼的那些陷阱

Lua2.4 內存分配 mem.c


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM