pymong 删除 mongogridfs 文件
一. 背景:
Gridfs 图片服务器存储
GridFS是MongoDB中存储和查询超过BSON文件大小限制(16M)的规范,不像BSON文件那样在一个单独的文档中存储文件,GridFS将文件分成多个块,每个块作为一个单独的文档。默认情况下,每个GridFS块是255kB,意味着除了最后一个块之外(根据剩余的文件大小),文档被分成多个255kB大小的块存储。
GridFS使用两个集合保存数据,一个集合存储文件块(fs.chunks),另外一个存储文件元数据(fs.files)。fs.chunks中每个chunk块大小为256KB,mongo的副本集和分片架构为GridFS提供了高效的读写扩展能力和高可用能力,在图片,视频,等大文件的存储方面具有较高的性能. [小文件,小文当(< 16M)保存在Bson二进制,大文件保存在gridfs].
公司项目图片服务器采用mongo副本集的gridfs来实现,上面存储jpg图片,mp3 音频,视频和sitemap文件. 近期图片服务器有一批一批的违规图片需要集中清理. 批量清理文件.
二. 当前问题:
mongofs gridfs 操作方案:
mongo文件系统gridfs 使用过程中,mongo提供了命令 mongofiles
进行管理。
[root@bj-test-wlj-2-132 twj]# mongofiles --help
Browse and modify a GridFS filesystem.
usage: mongofiles [options] command [gridfs filename]
command:
one of (list|search|put|get)
list - list all files. 'gridfs filename' is an optional prefix
which listed filenames must begin with.
search - search all files. 'gridfs filename' is a substring
which listed filenames must contain.
put - add a file with filename 'gridfs filename'
get - get a file with filename 'gridfs filename'
delete - delete all files with filename 'gridfs filename'
options:
--help produce help message
-v [ --verbose ] be more verbose (include multiple times
for more verbosity e.g. -vvvvv)
--version print the program's version and exit
-h [ --host ] arg mongo host to connect to ( <set
name>/s1,s2 for sets)
--port arg server port. Can also use --host
hostname:port
--ipv6 enable IPv6 support (disabled by
default)
-u [ --username ] arg username
-p [ --password ] arg password
--authenticationDatabase arg user source (defaults to dbname)
--authenticationMechanism arg (=MONGODB-CR)
authentication mechanism
--dbpath arg directly access mongod database files
in the given path, instead of
connecting to a mongod server - needs
to lock the data directory, so cannot
be used if a mongod is currently
accessing the same path
--directoryperdb each db is in a separate directly
(relevant only if dbpath specified)
--journal enable journaling (relevant only if
dbpath specified)
-d [ --db ] arg database to use
-c [ --collection ] arg collection to use (some commands)
-l [ --local ] arg local filename for put|get (default is
to use the same name as 'gridfs
filename')
-t [ --type ] arg MIME type for put (default is to omit)
-r [ --replace ] Remove other files with same name after
PUT
其中,删除文件操作:
mongofiles delete 文件名 --port 端口 --db 库名
mongofiles delete xxx.jpg --port 30000 --db pics
批量删除文件案例:
由于 需要删除的文件巨大,一批可能有5 ~ 6 十万,使用mongofiles 命令每次删除文件,都需要建立一次tcp连接. 于是临时把url放到文件中,然后差分文件,循环执行+sleep 缓解tcp端口占满:
for file in `ls`;do for i in `cat 31aa*`;do echo " mongofiles delete $i --port 30000 --db pics" >> 2021-8-31.log && mongofiles delete $i --port 30000 --db pic sleep 0.1 ;done ; echo sleep 60 ;sleep 120 ;done
风险点:
- 使用mongofiles 命令可以临时删除少量数据,且操作较慢
- 存在tcpdump数占满,mongo 连接数跑满的风险
- 在使用过程中发现,批量删除的文件量 大于 10000 就会出现删除失败的情况. mongofiles 提示操作成功,但文件依然存在
三. 解决方案:
开发临时解决:
由于shell命令批量处理异常文件,发现很多文件出现了操作失败的情况, 临时找开发进行处理。经了解,开发采用java语言使用mongo的驱动模块封装的方法remove,直接删除文件。
import com.mongodb.DB;
import com.mongodb.DBObject;
import com.mongodb.Mongo;
import com.mongodb.gridfs.GridFS;
import com.mongodb.gridfs.GridFSDBFile;
public class MyGridFsTest {
private String host = "127.0.0.1";
private int port = 27017;
private String dbName = "demogridfs";
@Test
public void testFindFile() throws IOException{
Mongo connection = new Mongo(host, port);
DB db = connection.getDB(dbName);
GridFS gridFs = new GridFS(db);
DBObject query = new BasicDBObject("filename", fileName);
gridFs.remove(query);
java 的Mongo驱动二次封装直接删除文件,而且采用mongo线程池,不过多的占用Mongo连接数等资源。
python pymongo 解决方案:
(pymongo) [root@bj-redis-slave02-10-8-2-245 history]# python delete-pic.py > 2021-8.log &
(pymongo) [root@bj-redis-slave02-10-8-2-245 history]# tail -f 2021-8.log
四. 附: 代码:
#!/usr/bin/python
# -*- encoding: utf-8 -*-
import pymongo
import json
import os
from pymongo import MongoClient
from gridfs import GridFS
class GFS(object):
def __init__(self, file_db,file_table):
self.file_db = file_db
self.file_table = file_table
def createDB(self): #连接数据库,并创建文件数据库与数据表
client = MongoClient('10.8.2.237',30000)
db = client[self.file_db]
file_table = db[self.file_table]
return (db,file_table)
def insertFile(self,db,filePath,query): #将文件存入数据表
fs = GridFS(db,self.file_table)
if fs.exists(query):
print('已经存在该文件')
else:
with open(filePath,'rb') as fileObj:
data = fileObj.read()
ObjectId = fs.put(data,filename = filePath.split('/')[-1])
print(ObjectId)
fileObj.close()
return ObjectId
def getID(self,db,query,pic): #通过文件属性获取文件ID,ID为文件删除、文件读取做准备
try:
fs=GridFS(db, self.file_table)
ObjectId=fs.find_one(query)._id
msg=pic,'ObjectId',ObjectId
print (msg)
return ObjectId
except AttributeError as e: #AttributeError为错误类型,此种错误的类型赋值给变量e;当try与except之间的语句触发
# AttributeError错误时程序不会异常退出而是执行except AttributeError下面的内容
print("AttributeError错误,图片不存在:",e)
def remove(self,db,id): #文件数据库中数据的删除
fs = GridFS(db, self.file_table)
fs.delete(id) #只能是id
del_msg= id,'正在删除!!!'
print (del_msg)
def getFile(self,db,id): #获取文件属性,并读出二进制数据至内存
fs = GridFS(db, self.file_table)
gf=fs.get(id)
bdata=gf.read() #二进制数据
attri={} #文件属性信息
attri['chunk_size']=gf.chunk_size
attri['length']=gf.length
attri["upload_date"] = gf.upload_date
attri["filename"] = gf.filename
attri['md5']=gf.md5
print(attri)
return (bdata, attri)
def Writ_log(self,File,msg):
f=open(File,'a')
f.write(msg)
f.close()
def listFile(self,db): #列出所有文件名
fs = GridFS(db, self.file_table)
gf = fs.list()
def findFile(self,db,file_table): #列出所有文件二进制数据
fs = GridFS(db, table)
for file in fs.find():
bdata=file.read()
def write_2_disk(self,bdata, attri): #将二进制数据存入磁盘
name = "get_"+attri['filename']
if name:
output = open(name, 'wb')
output.write(bdata)
output.close()
print("fetch image ok!")
if __name__=='__main__':
gfs=GFS('pics','fs')
(file_db,fileTable) = gfs.createDB() #创建数据库与数据表
dir_list = os.listdir('2021-8')
print (dir_list)
for filePath in dir_list:
filePath='2021-8/'+filePath
lines = open(filePath,'r').readlines()
for file in lines:
pic =file.splitlines()[0]
query = '{"filename": "%s"}' %(pic)
query = json.loads(query)
id=gfs.getID(file_db,query,pic)
gfs.remove(file_db,id) #删除数据库中文件
#filePath = '10.txt' #插入的文件
#query = {'filename': '745082993188.jpg'}
#id=gfs.getID(file_db,query,pic)
#gfs.remove(file_db,id) #删除数据库中文件
#id=gfs.insertFile(file_db,filePath,query) #插入文件
#(bdata,attri)=gfs.getFile(file_db,id) #查询并获取文件信息至内存
#gfs.write_2_disk(bdata,attri) #写入磁盘
#gfs.remove(file_db,id) #删除数据库中文件
参考:
pymongo 对mongo nosql的操作 教程较多,多gridfs 文件系统的上传下载介绍较多。但对gridfs delete操作 介绍较少。核心重要的操作是,先获取文件的文件id,然后通过remove方法移除文件id。(remove 文件方法只接受文件id)
官方: https://pymongo.readthedocs.io/en/stable/tutorial.html
gridfs delete 官方: https://pymongo.readthedocs.io/en/stable/api/gridfs/index.html?highlight=delete#gridfs.GridFS.delete
https://blog.csdn.net/qq_30852577/article/details/84645693
https://blog.csdn.net/weiyuanke/article/details/7717476