由于kodbox是使用时间作为存储路径的,这样就没办法直接在后台复制文件。

并且使用挂载方式的话,速度挺慢的。

想迁移到别的软件中也特别的麻烦。

所以计划做一个优化,将本地存储与数据库中保持一致。

优化:
删除表内存在但是文件不存在的记录
记录最后修改时间,根据修改时间,来对修改过的文件,本地进行优化。
获取当前文件目录
移动文件到正确的目录
修改io表中的文件位置。

经过前面修复数据库的经历,这个应该是不成问题。

稍微再修改一下代码。

#!/usr/bin/python3

import pymysql
import datetime
import os
import sys
import time

sys.path.append("/config/workspace/code/python/") 
from public import filectl as fc


# 资料
# io_file
# fileID    文件的唯一id    9280
# path      文件的路径      HEIF-Utility.zip
# name      文件名称        {io:1}/202102/23_16c7fd2e/HEIF-Utility.zip

# io_source
# sourceID  资源的标识,可能是文件,也可以是文件夹
# fileID    文件的话是io_file中的fileID,文件夹的话是0
# name      文件夹或者文件的名称,如果kodbox中修改文件名,实际文件是不会修改的,只会修改这里。
# isFolder  是否是文件夹
# parentID  父路径
# parentLevel   全路径,格式为:,0,5,7,1179,2847,10931,10939,每个数字都是一个sourceID 包含自己
# modifyTime    最后修改时间,可以用来判断是否需要优化路径
# targetType    文档所属类型 (0-sys,1-user,2-group)  只考虑用户的文件其他的不管



# 方案设计:
# 先读取所有的io_source中的文件夹
# 计算出来所有的parentID的实际路径
# 逐个创建所有的文件夹
# 逐个移动文件
# 文件只会增加不会降低,所以只需要记录最高的fileid就可以了。其实不需要关心io表

# 情景:
# 1.文件或者文件夹已经不存在了,需要删除本地,数据库中是否一起删除。【不删除】
# 2.根据最后修改时间来每次优化数据
# 3.文件夹修改了,改如何实现。删除原本的文件夹。

# 默认目录/config/workspace
#  os.system("pwd")

# 打开数据库连接
conn = pymysql.connect(host='192.168.16.11',port=3306,user='kodbox', password='password',database='kodbox',charset='utf8')

# 使用 cursor() 方法创建一个游标对象 cursor
cursor = conn.cursor()
 
# 使用 execute()  方法执行 SQL 查询 
cursor.execute("use kodbox")

# 资源id到实际路径的map
dirID2path={0:''}

# 保存文件的路径
savebasedir = '/kodboxfile'

# 源数据的文件路径
loadbasedir = '/kodboxfile'

startid = 0

# 先读取之前的配置
# 如果文件存在,则直接读取。读取之前的文件结构,当数据库中文件结构发生变化的时候,可以同步使本地也进行修改。
if os.path.exists("/config/workspace/code/python/movekodboxdb/movefiledir.txt"):
    dirID2path = fc.loadjson_fromfile('/config/workspace/code/python/movekodboxdb/movefiledir.txt')

mxset={}
mxset=fc.loadjson_fromfile('/config/workspace/code/python/movekodboxdb/mxset.txt')
modifytime = mxset['modifyTime']

# 先处理文件夹,保证文件可以移动。
while 1:
    sql = "SELECT sourceID,parentID,name FROM io_source WHERE isFolder = 1 and targetType = 1 and modifyTime > " + str(modifytime) + " ORDER BY modifyTime,parentID,sourceID LIMIT " + str(startid) + ',100'
    cursor.execute(sql)

    results = cursor.fetchall()
    if len(results) <= 0:
        break
    
    for row in results:
        sourceID = row[0]
        parentID = row[1]
        name = row[2]

        path = dirID2path[parentID] + '/' + name
        # 如果没有记录了,说明是新增的。
        if sourceID not in dirID2path.keys():
            # 新增的需要创建文件夹
            if not os.path.exists(savebasedir + path):
                os.makedirs(savebasedir + path)
                print("creat " + savebasedir + path)
        else:
            # 否则就是修改了
            oldpath = savebasedir + dirID2path[sourceID]
            newpath = savebasedir + path

            # 由于修改子文件夹,父文件夹也会有修改时间的变化,所以进行判断。
            if oldpath != newpath:
                os.system("mv \"" + oldpath + '\" \"' + newpath + "\"")
                print("mv " + oldpath + " " + newpath)
            # print("mod " + oldpath + " " + newpath)
        # 记录或者更新数据    
        dirID2path[sourceID] = path
    startid += 100


# 进行存档
mxset['modifyTime'] = int(time.time())
fc.save_to_txt(mxset,'/config/workspace/code/python/movekodboxdb/mxset.txt')
fc.save_to_txt(dirID2path,'/config/workspace/code/python/movekodboxdb/movefiledir.txt')

# 第二步,逐个移动文件

# 获取文件所有的文件的真实路径,然后进行move。

# 获取源文件目录,移动文件
startid = 0

# 源文件不存在的
notexis = []

# 目标文件已经存在的
isexis = []

# 父文件夹没有创建的
unknowndir=[]

while 1:
    sql = "SELECT io_source.parentID,io_source.name,io_file.name,io_file.path,io_source.fileID FROM io_source,io_file WHERE " + \
    "io_source.isFolder = 0 and io_source.fileID = io_file.fileID and io_source.modifyTime > " + str(modifytime) + \
    " ORDER BY io_source.fileID LIMIT " + str(startid) + ',100'
    cursor.execute(sql)
    results = cursor.fetchall()
    if len(results) <= 0:
        break

    # 修改分为三种
    # 一、修改文件内容,不需要操作
    # 二、修改文件路径,需要移动文件
    # 三、修改文件名字,需要重命名
    for row in results:
        # 父路径id
        parentID = row[0]
        # 源文件名字
        srcname = row[1]
        # 现在文件名字
        nowname = row[2]
        # 源文件的路径
        # {io:1}/202102/23_16c7fd2e/HEIF-Utility.zip
        path = row[3]
        # fileid,用来修改数据库
        fileID = row[4]

        # 如果父id不存在,那么就进行记录,并输出。
        if parentID not in dirID2path.keys():
            unknowndir.append(str(parentID) + '--' + path[6:])
            continue
        
        # 原路径
        srcpath = loadbasedir + path[6:]
        # 目标路径
        dstpath = savebasedir + dirID2path[parentID] + '/' + nowname

        # 如果路径变化了,那么就进行移动
        if srcpath != dstpath:
            # 如果源文件不在进行提示
            if not os.path.exists(srcpath):
                print(srcpath + 'not exist')
                notexis.append(str(fileID) + '--' + srcpath + '--' + dstpath)
                continue

            # 这样会产生一个问题,因为kodbox支持历史版本的,他历史版本是通过多个文件实现的
            # 原本的逻辑,如果存在同名的,会重新进行命名,如果我们也这么做的话,会导致本地存在多个文件。
            # 如果不这么做的话,那么kodbox新产生的文件就会跟旧文件同名,直接mv就会造成历史版本文件被覆盖掉。
            # 暂时先不考虑历史版本问题,暂时没有好的解决方案。
            # 有个想法,就是给文件夹下面新建一个文件夹,就叫历史版本,然后给所有的其他历史版本都移动到这个文件夹下面并改名为历史版本1,2,3等的
            # 然后逐个修改数据库,在移动之前,先处理好历史版本,这样就可以保证最新的文件在当前文件夹下面,并且不会丢失历史版本。
            os.system("mv \"" + srcpath + '\" \"' + dstpath + "\"")
            # 还需要修改数据库
            dbnewpath = '{io:1}' + dirID2path[parentID] + '/' + nowname
            # UPDATE io_file SET path='{io:1}/202112/19_fb23a55f/2.txt' WHERE fileID = 12
            sql = 'UPDATE io_file SET path=\'' + dbnewpath + '\' WHERE fileID = ' + str(fileID)
            print(sql)
            cursor.execute(sql)
            # 提交到数据库执行
            conn.commit()

    startid += 100

mxset['modifyTime'] = int(time.time())
fc.save_to_txt(mxset,'/config/workspace/code/python/movekodboxdb/mxset.txt')
fc.save_to_txt(unknowndir,'/config/workspace/code/python/movekodboxdb/moveunknown.txt')
fc.save_to_txt(isexis,'/config/workspace/code/python/movekodboxdb/moveisexis.txt')
fc.save_to_txt(notexis,'/config/workspace/code/python/movekodboxdb/movenotexis.txt')

# print(unknowndir)

# 关闭光标
cursor.close()

# 关闭数据库连接
conn.close()

print(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S succeed"))

但是这个程序要怎么样才能每天定时调用呢?这是个问题。

之前做相册自动同步的时候,我们外部启用了一个定时器,来定时调用docker中的代码,从而实现定时索引照片。

所以我们也可以同样做一个类似的功能,通过定时调用code-server中的python来实现自动化优化kodbox数据库。

为了方便之后修改,我计划采用调用一个脚本来调用python,这样的话,就算以后需要增加自动调用,也就只需要修改sh脚本即可。

经过测试没有问题。

下面就是加入到unraid的定时计划中。

打开go文件,增加定时计划

echo "0 5 * * * docker exec -i code-server /bin/bash /config/workspace/data/shell/run.sh" >> /etc/cron.d/root  &

然后 修改一下run.sh脚本

python3 /config/workspace/code/python/movekodboxdb/movefile.py >> /config/workspace/code/python/run/movefile.res

然后先暂时手动执行一下试试看

应该是没有问题的。

后续问题:

1.删除本地怎么同步?

由于原文件的指向没有问题,当你真正删除后(回收站也删除了),那么文件也会被删除掉,这个没有问题。

2.秒传问题

kodbox是通过引用计数来进行存储的,也就说,你同一个文件,分别保存在不同的位置,其实原文件就只有一份,这个也就是秒传的由来。在这个情况下,如果你上传了第二次,调用优化后,肯定会把第一个文件给移动到第二个位置,这样的话,在kodbox中查看,两个地方将会有两份,实际上却只有一个文件。优化同步后,本地之后在最后一个位置存在,之前的会被移走,这种情况暂时没有想法。目前只能通过尽量减少上传相同文件来解决。如果有想法的,欢迎留言。

3.文件夹问题

由于kodbox本身是没有文件夹的,所以当你删除文件以及文件夹的时候,文件会被删除掉,但是文件夹会被保留下来,所以需要自己清理一下。目前暂未实现,大致想法是,优化执行结束后,我们已经有了最新的文件夹结构了,这个时候,与本地做一下对比,如果本地存在,并且文件夹为空,而kodbox中已经不存在该路径了,那么就可以进行删除。

另外,数据无价,谨慎操作,建议先拿数据测试,没有问题了再执行。

所有代码中不含有删除操作,仅有mv操作,一般来说不会数据丢失。

码代码不易,如果此文章帮到了你,欢迎打赏,一份也是爱。


一沙一世界,一花一天堂。君掌盛无边,刹那成永恒。