local mod_name = (...):match ( "^(.*)%..-$" ) local md5 = require "resty.md5" local str = require "resty.string" local bson = require ( mod_name .. ".bson" ) local gridfs_file_mt = { } local gridfs_file = { __index = gridfs_file_mt } local get_bin_data = bson.get_bin_data -- write size bytes from the buf string into mongo, by the offset function gridfs_file_mt:write(buf, offset, size) size = size or string.len(buf) if offset > self.file_size then return nil, "invalid offset" end if size > #buf then return nil, "invalid size" end local cn -- number of chunks to be updated local af -- number of bytes to be updated in first chunk local bn = 0 -- bytes number of buf already updated local nv = {} local od, t, i, r, err local of = offset % self.chunk_size local n = math.floor(offset/self.chunk_size) if of == 0 and size % self.chunk_size == 0 then -- chunk1 chunk2 chunk3 -- old data ====== ====== ====== -- write buf ====== ====== -- -- old data ====== ====== ====== -- write buf ====== cn = size/self.chunk_size for i = 1, cn do nv["$set"] = {data = get_bin_data(string.sub(buf, self.chunk_size*(i-1) + 1, self.chunk_size*(i-1) + self.chunk_size))} r, err = self.chunk_col:update({files_id = self.files_id, n = n+i-1}, nv, 1, 0, true) if not r then return nil,"write failed: "..err end end bn = size else if of + size > self.chunk_size then -- chunk1 chunk2 chunk3 -- old data ====== ====== ====== -- write buf ======= -- ... -> of -- ... -> af af = self.chunk_size - of else af = size end cn = math.ceil((size + offset)/self.chunk_size) - n for i = 1, cn do if i == 1 then od = self.chunk_col:find_one( {files_id = self.files_id, n = n+i-1}) if of ~= 0 and od then if size + of >= self.chunk_size then -- chunk1 chunk2 chunk3 -- old data ====== ====== ====== -- write buf ===== t = string.sub(od.data, 1, of) .. string.sub(buf, 1, af) else -- chunk1 chunk2 chunk3 -- old data ====== ====== ====== -- write buf == t = string.sub(od.data, 1, of) .. string.sub(buf, 1, af) .. string.sub(od.data, size + of + 1) end bn = af elseif of == 0 and od then if size < self.chunk_size then -- chunk1 chunk2 chunk3 -- old data ====== ====== ====== -- write buf === t = string.sub(buf, 1) .. string.sub(od.data, size + 1) bn = bn + size else -- chunk1 chunk2 chunk3 -- old data ====== ====== ====== -- write buf ========= t = string.sub(buf, 1, self.chunk_size) bn = bn + self.chunk_size end else t = string.sub(buf, 1, self.chunk_size) bn = bn + #t --self.chunk_size end nv["$set"] = {data = get_bin_data(t)} r,err = self.chunk_col:update({files_id = self.files_id, n = n+i-1}, nv, 1, 0, true) if not r then return nil,"write failed: "..err end elseif i == cn then od = self.chunk_col:find_one( {files_id = self.files_id, n = n + i - 1} ) if od then t = string.sub(buf, bn + 1, size) .. string.sub(od.data, size - bn + 1) else t = string.sub(buf, bn + 1, size) end nv["$set"] = {data = get_bin_data(t)} r,err = self.chunk_col:update({files_id = self.files_id, n = n+i-1}, nv, 1, 0, true) if not r then return nil,"write failed: "..err end bn = size else nv["$set"] = {data = get_bin_data(string.sub(buf, bn + 1, bn + self.chunk_size))} r,err = self.chunk_col:update({files_id = self.files_id, n = n+i-1}, nv, 1, 0, true) if not r then return nil,"write failed: "..err end bn = bn + self.chunk_size end end end local nf = offset + bn if nf > self.file_size then nv["$set"] = {length = nf} r,err = self.file_col:update({_id = self.files_id},nv, 0, 0, true) if not r then return nil,"write failed: "..err end end nv["$set"] = {md5 = 0} r,err = self.file_col:update({_id = self.files_id},nv, 0, 0, true) if not r then return nil,"write failed: "..err end return bn end -- read size bytes from mongo by the offset function gridfs_file_mt:read(size, offset) size = size or self.file_size if size < 0 then return nil, "invalid size" end offset = offset or 0 if offset < 0 or offset >= self.file_size then return nil, "invalid offset" end local n = math.floor(offset / self.chunk_size) local r local bytes = "" local rn = 0 while true do r = self.chunk_col:find_one({files_id = self.files_id, n = n}) if not r then return nil, "read chunk failed" end if size - rn < self.chunk_size then bytes = bytes .. string.sub(r.data, 1, size - rn) rn = size else bytes = bytes .. r.data rn = rn + self.chunk_size end n = n + 1 if rn >= size then break end end return bytes end function gridfs_file_mt:update_md5() local n = math.floor(self.file_size/self.chunk_size) local md5_obj = md5:new() local r, i, err for i = 0, n do r = self.chunk_col:find_one({files_id = self.files_id, n = i}) if not r then return false, "read chunk failed" end md5_obj:update(r.data) end local md5hex = str.to_hex(md5_obj:final()) local nv = {} nv["$set"] = {md5 = md5hex} self.file_md5 = md5hex r,err = self.file_col:update({_id = self.files_id}, nv, 0, 0, true) if not r then return false, "update failed: "..err end return true end return gridfs_file