178 lines
5.6 KiB
Plaintext
178 lines
5.6 KiB
Plaintext
from PDAPI import *
|
|
import os
|
|
import json
|
|
import hashlib
|
|
import shutil
|
|
import time
|
|
|
|
RESOURCE_ROOT = os.path.join(os.path.abspath(os.path.join(os.getcwd(), "resource")))
|
|
TEMP_ROOT = os.path.join(RESOURCE_ROOT, ".tmp")
|
|
|
|
|
|
def _sha1_path(sha1_hex):
|
|
return os.path.join(RESOURCE_ROOT, sha1_hex[0:2], sha1_hex[2:4], sha1_hex[4:6], sha1_hex)
|
|
|
|
|
|
def _ensure_dir(path):
|
|
os.makedirs(os.path.dirname(path), exist_ok=True)
|
|
|
|
|
|
def _format_size(size):
|
|
if size < 1024:
|
|
return f"{size} B"
|
|
elif size < 1024 * 1024:
|
|
return f"{size / 1024:.1f} KB"
|
|
elif size < 1024 * 1024 * 1024:
|
|
return f"{size / (1024 * 1024):.1f} MB"
|
|
else:
|
|
return f"{size / (1024 * 1024 * 1024):.1f} GB"
|
|
|
|
|
|
def _cleanup_temp(temp_dir):
|
|
try:
|
|
shutil.rmtree(temp_dir, ignore_errors=True)
|
|
except:
|
|
pass
|
|
|
|
|
|
__metadata__ = {
|
|
"methods": ["POST"],
|
|
"blueprint": "GERP",
|
|
"permission": 3.0,
|
|
"attributes": [],
|
|
"limit": "10 per minute",
|
|
}
|
|
|
|
def HandleRequest(_CleanedData):
|
|
current_user = UGM.GetELTUser()
|
|
if not current_user:
|
|
return {"code": 401, "msg": "未登录"}, 401
|
|
|
|
data = flask.request.get_json(silent=True) or {}
|
|
upload_id = data.get("uploadId", "").strip()
|
|
|
|
if not upload_id:
|
|
return {"code": 1, "msg": "缺少uploadId"}, 400
|
|
|
|
temp_dir = os.path.join(TEMP_ROOT, upload_id)
|
|
if not os.path.isdir(temp_dir):
|
|
return {"code": 1, "msg": "上传会话不存在或已过期"}, 404
|
|
|
|
meta_path = os.path.join(temp_dir, "meta.json")
|
|
if not os.path.exists(meta_path):
|
|
_cleanup_temp(temp_dir)
|
|
return {"code": 1, "msg": "上传元数据不存在"}, 404
|
|
|
|
with open(meta_path, "r", encoding="utf-8") as f:
|
|
meta = json.load(f)
|
|
|
|
total_chunks = meta["totalChunks"]
|
|
received = sorted(meta.get("receivedChunks", []))
|
|
expected = list(range(total_chunks))
|
|
|
|
if received != expected:
|
|
missing = [i for i in expected if i not in received]
|
|
return {"code": 1, "msg": f"分片不完整,缺少: {missing}"}, 400
|
|
|
|
sha1 = hashlib.sha1()
|
|
merged_size = 0
|
|
final_path = os.path.join(temp_dir, "merged")
|
|
|
|
with open(final_path, "wb") as out_f:
|
|
for i in range(total_chunks):
|
|
chunk_path = os.path.join(temp_dir, str(i))
|
|
if not os.path.exists(chunk_path):
|
|
_cleanup_temp(temp_dir)
|
|
return {"code": 1, "msg": f"分片{i}文件丢失"}, 400
|
|
with open(chunk_path, "rb") as in_f:
|
|
while True:
|
|
block = in_f.read(65536)
|
|
if not block:
|
|
break
|
|
sha1.update(block)
|
|
out_f.write(block)
|
|
merged_size += len(block)
|
|
|
|
sha1_hex = sha1.hexdigest()
|
|
|
|
existing = GERPFileAsset.query.filter(GERPFileAsset.FileSHA1 == sha1_hex).first()
|
|
if existing:
|
|
_cleanup_temp(temp_dir)
|
|
return {
|
|
"code": 0,
|
|
"msg": "文件已存在(秒传)",
|
|
"data": {
|
|
"assetUUID": UUIDObj(existing.AssetUUID).UpperStr,
|
|
"fileName": existing.FileName,
|
|
"fileSize": existing.FileSize,
|
|
"fileSizeDisplay": _format_size(existing.FileSize),
|
|
"mimeType": existing.MimeType,
|
|
"sha1": existing.FileSHA1,
|
|
"refLink": f"https://api.gvsds.com/Asset/Download/{existing.FileSHA1}",
|
|
"createTime": existing.CreateTime.strftime("%Y-%m-%d %H:%M:%S") if existing.CreateTime else None,
|
|
}
|
|
}, 200
|
|
|
|
storage_path = _sha1_path(sha1_hex)
|
|
_ensure_dir(storage_path)
|
|
|
|
try:
|
|
shutil.move(final_path, storage_path)
|
|
except shutil.Error:
|
|
with open(final_path, "rb") as src, open(storage_path, "wb") as dst:
|
|
shutil.copyfileobj(src, dst)
|
|
os.remove(final_path)
|
|
|
|
for i in range(total_chunks):
|
|
chunk_path = os.path.join(temp_dir, str(i))
|
|
if os.path.exists(chunk_path):
|
|
os.remove(chunk_path)
|
|
|
|
asset_uuid = NewBUUID()
|
|
new_asset = GERPFileAsset()
|
|
new_asset.AssetUUID = asset_uuid
|
|
new_asset.FileName = meta["fileName"]
|
|
new_asset.FileSHA1 = sha1_hex
|
|
new_asset.FileSize = merged_size
|
|
new_asset.MimeType = meta["mimeType"]
|
|
new_asset.StoragePath = storage_path
|
|
new_asset.UploaderUUID = current_user.UserUUID
|
|
new_asset.RefCount = 0
|
|
|
|
current_role_uuid_str = flask.request.cookies.get("CurrentRoleUUID")
|
|
if current_role_uuid_str:
|
|
try:
|
|
role_uuid_bytes = UUIDObj(current_role_uuid_str).bytes
|
|
current_role = GERPRole.query.filter(GERPRole.RoleUUID == role_uuid_bytes).first()
|
|
if current_role and current_role.CompanyUUID:
|
|
new_asset.CompanyUUID = current_role.CompanyUUID
|
|
except:
|
|
pass
|
|
|
|
try:
|
|
db.session.add(new_asset)
|
|
db.session.commit()
|
|
except:
|
|
db.session.rollback()
|
|
if os.path.exists(storage_path):
|
|
os.remove(storage_path)
|
|
_cleanup_temp(temp_dir)
|
|
return {"code": 1, "msg": "数据库写入失败"}, 200
|
|
|
|
_cleanup_temp(temp_dir)
|
|
|
|
return {
|
|
"code": 0,
|
|
"msg": "上传成功",
|
|
"data": {
|
|
"assetUUID": UUIDObj(asset_uuid).UpperStr,
|
|
"fileName": new_asset.FileName,
|
|
"fileSize": new_asset.FileSize,
|
|
"fileSizeDisplay": _format_size(new_asset.FileSize),
|
|
"mimeType": new_asset.MimeType,
|
|
"sha1": new_asset.FileSHA1,
|
|
"refLink": f"https://api.gvsds.com/Asset/Download/{new_asset.FileSHA1}",
|
|
"createTime": new_asset.CreateTime.strftime("%Y-%m-%d %H:%M:%S") if new_asset.CreateTime else None,
|
|
}
|
|
}, 201
|