|
@@ -13,6 +13,7 @@ from django.contrib.auth.hashers import make_password
|
|
|
from io import TextIOWrapper, BytesIO
|
|
|
from ast import literal_eval
|
|
|
from typing import Any
|
|
|
+import zipfile
|
|
|
|
|
|
types = [
|
|
|
('csv', 'csv'),
|
|
@@ -93,15 +94,24 @@ class FileManager(models.Manager):
|
|
|
directory = os.path.join(BASE_FILE_PATH, str(user.id))
|
|
|
path = os.path.join(directory, str(fileId))
|
|
|
|
|
|
- try:
|
|
|
- size = os.path.getsize(path)
|
|
|
- except FileNotFoundError:
|
|
|
- print("未找到对应文件,现将记录删除", fileId, file.name)
|
|
|
- self.get(id=fileId).delete()
|
|
|
- continue
|
|
|
- except Exception as error:
|
|
|
- print("读取历史记录时出现未知错误")
|
|
|
- return FAILED
|
|
|
+ # 首先检查文件是否被归档压缩
|
|
|
+ archivePath = os.path.join(BASE_FILE_PATH, "archive.zip")
|
|
|
+ with zipfile.ZipFile(archivePath, 'a') as zipf:
|
|
|
+ fileArchPath = os.path.normpath(f"{file.user.id}/{file.id}").replace("\\", "/")
|
|
|
+ if fileArchPath in zipf.namelist():
|
|
|
+ # 重复添加压缩,则跳过压缩步骤直接将原始文件删除即可
|
|
|
+ size = zipf.getinfo(fileArchPath).file_size
|
|
|
+ else:
|
|
|
+ # 文件未被压缩,查找是否真的有文件存在,否则清理掉没有实际文件的数据库记录
|
|
|
+ try:
|
|
|
+ size = os.path.getsize(path)
|
|
|
+ except FileNotFoundError:
|
|
|
+ print("未找到对应文件,现将记录删除", fileId, file.name)
|
|
|
+ self.get(id=fileId).delete()
|
|
|
+ continue
|
|
|
+ except Exception as error:
|
|
|
+ print("读取历史记录时出现未知错误")
|
|
|
+ return FAILED
|
|
|
|
|
|
if size >= 1024 * 1024:
|
|
|
size = size / (1024 * 1024)
|
|
@@ -151,6 +161,7 @@ class File(models.Model):
|
|
|
update_time = models.DateTimeField(auto_now=True)
|
|
|
content = models.CharField(choices=contents, max_length=10)
|
|
|
encrypted = models.BooleanField(default=False)
|
|
|
+ archived = models.BooleanField(default=False)
|
|
|
key = models.CharField(blank=True, null=True, max_length=128)
|
|
|
associate = models.ForeignKey('self', on_delete=models.CASCADE, blank=True, null=True)
|
|
|
|
|
@@ -252,11 +263,71 @@ class File(models.Model):
|
|
|
return False
|
|
|
return True
|
|
|
|
|
|
-
|
|
|
+ def archive(self):
|
|
|
+ if not self.archived:
|
|
|
+ filePath = os.path.join(os.path.join(BASE_FILE_PATH, str(self.user.id)), str(self.id))
|
|
|
+ archivePath = os.path.join(BASE_FILE_PATH, "archive.zip")
|
|
|
+ try:
|
|
|
+ with zipfile.ZipFile(archivePath, 'a') as zipf:
|
|
|
+ fileArchPath = os.path.normpath(f"{self.user.id}/{self.id}").replace("\\", "/")
|
|
|
+ if fileArchPath in zipf.namelist():
|
|
|
+ # 重复添加压缩,则跳过压缩步骤直接将原始文件删除即可
|
|
|
+ self.archived = True
|
|
|
+ self.save()
|
|
|
+ os.remove(filePath)
|
|
|
+ else:
|
|
|
+ # 使用用户id和文件id组合成压缩文件中索引
|
|
|
+ zipf.write(filePath, fileArchPath)
|
|
|
+ self.archived = True
|
|
|
+ self.save()
|
|
|
+ os.remove(filePath)
|
|
|
+ except Exception as error:
|
|
|
+ logger.error(f"压缩文件{self.id} {self.name}失败: {error}")
|
|
|
+ else:
|
|
|
+ pass
|
|
|
|
|
|
+ def unzip(self):
|
|
|
+ if not self.archived:
|
|
|
+ self.error(f"解压文件{self.id} {self.name}失败,文件并未压缩")
|
|
|
+ return
|
|
|
+ else:
|
|
|
+ filePath = os.path.join(os.path.join(BASE_FILE_PATH, str(self.user.id)), str(self.id))
|
|
|
+ archivePath = os.path.join(BASE_FILE_PATH, "archive.zip")
|
|
|
+ try:
|
|
|
+ with zipfile.ZipFile(archivePath, 'r') as zipf:
|
|
|
+ fileArchPath = os.path.normpath(f"{self.user.id}/{self.id}").replace("\\", "/")
|
|
|
+ if fileArchPath in zipf.namelist():
|
|
|
+ with zipf.open(fileArchPath) as zipd:
|
|
|
+ content = zipd.read()
|
|
|
+ with open(filePath, 'wb') as f:
|
|
|
+ f.write(content)
|
|
|
+ # 恢复压缩标记
|
|
|
+ self.archived = False
|
|
|
+ self.save()
|
|
|
+ else:
|
|
|
+ raise ValueError(f"该文件不存在压缩文件中")
|
|
|
+ except Exception as error:
|
|
|
+ logger.error(f"解压缩文件{self.id} {self.name}失败:{error}")
|
|
|
|
|
|
def download(self):
|
|
|
path = os.path.join(os.path.join(BASE_FILE_PATH, str(self.user.id)), str(self.id))
|
|
|
+ archivePath = os.path.join(BASE_FILE_PATH, "archive.zip")
|
|
|
+ # 需要检查文件是否被归档,下载归档文件并不需要解压
|
|
|
+ if self.archived:
|
|
|
+ with zipfile.ZipFile(archivePath, 'r') as zipf:
|
|
|
+ fileArchPath = os.path.normpath(f"{self.user.id}/{self.id}").replace("\\", "/")
|
|
|
+ if fileArchPath in zipf.namelist():
|
|
|
+ # 在压缩文件中找到文件,将其数据读出用于下载
|
|
|
+ with zipf.open(fileArchPath) as zfile:
|
|
|
+ logger.info("从压缩包中下载")
|
|
|
+ content = zfile.read()
|
|
|
+ response = FileResponse(BytesIO(content))
|
|
|
+ response['Content-Disposition'] = f'attachment; filename="{self.name}"'
|
|
|
+ return FileResponse(open(path, 'rb'))
|
|
|
+ else:
|
|
|
+ logger.info(f"文件{self.id} {self.name}具有压缩标记,但未在压缩文件中找到")
|
|
|
+ raise ValueError(f"文件{self.id} {self.name}具有压缩标记,但未在压缩文件中找到")
|
|
|
+
|
|
|
if not os.path.exists(path):
|
|
|
return False
|
|
|
# 加密后文件也不允许下载
|
|
@@ -350,6 +421,7 @@ class File(models.Model):
|
|
|
return UNKNOWN_CONTENT
|
|
|
|
|
|
def storage(self, file):
|
|
|
+ # 将file数据保存成文件,不对file做任何处理
|
|
|
try:
|
|
|
path = os.path.join(BASE_FILE_PATH, str(self.user.id))
|
|
|
if os.path.exists(os.path.join(path, str(self.id))):
|
|
@@ -374,6 +446,10 @@ class File(models.Model):
|
|
|
|
|
|
# 检查文件是否合法
|
|
|
def checkIllegal(self):
|
|
|
+ # 检查文件前需要检查是否被压缩,如被压缩则需要解压
|
|
|
+ if self.archived:
|
|
|
+ self.unzip()
|
|
|
+
|
|
|
path = os.path.join(os.path.join(BASE_FILE_PATH, str(self.user.id)), str(self.id))
|
|
|
path2 = os.path.join(os.path.join(BASE_FILE_PATH, str(self.user.id)), str(self.associate.id))
|
|
|
if self.content == 'node':
|
|
@@ -429,6 +505,10 @@ class File(models.Model):
|
|
|
return True
|
|
|
|
|
|
def toJson(self, request=None):
|
|
|
+ # 需要检查文件是否被归档压缩,如有则需要先解压
|
|
|
+ if self.archived:
|
|
|
+ self.unzip()
|
|
|
+
|
|
|
# 检查是否为加密文件,只有当文件usage为input时才应该存在加密属性
|
|
|
if self.usage == 'input' and self.encrypted:
|
|
|
# 如果被加密则需要从request中获取解密密钥
|