You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

194 lines
7.2 KiB
Lua

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