finish copy files follow bytes sort
This commit is contained in:
26
fake_main.py
Normal file
26
fake_main.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from files_utils.files_list import GetFilesDBNodeInfo, GetFilesDBPathInfo, SortFragmentsByStartByte
|
||||
from files_utils.files_save import CopyFileFromBytes
|
||||
|
||||
|
||||
def GetSortFragments(files_list: list) -> list:
|
||||
path_info = GetFilesDBPathInfo(db_path="./src/db_ntfs_info.db", table_name="db_path", files_path=files_list)
|
||||
node_info = GetFilesDBNodeInfo(db_path="./src/db_ntfs_info.db", table_name="db_node", path_records=path_info)
|
||||
result = SortFragmentsByStartByte(node_info)
|
||||
return result
|
||||
|
||||
|
||||
test_files = [
|
||||
r"Y:\CloudMusic\AGA - MIZU.mp3",
|
||||
r"Y:\CloudMusic\AGA - 一.mp3",
|
||||
r"Y:\CloudMusic\Aaron Zigman - Main Title.mp3",
|
||||
r"Y:\CloudMusic\Anson Seabra - Keep Your Head Up Princess.mp3",
|
||||
r"Y:\CloudMusic\Anthony Keyrouz,Romy Wave - Something Just Like This (feat. Romy Wave).mp3",
|
||||
r"Y:\CloudMusic\Ava Max - Sweet but Psycho.mp3",
|
||||
r"Y:\CloudMusic\Cecilia Cheung - Turn Into Fireworks and Fall for You.mp3",
|
||||
r"Y:\CloudMusic\Color Music Choir - Something Just Like This (Live).mp3"
|
||||
]
|
||||
|
||||
sort_fragments = GetSortFragments(test_files)
|
||||
for item in sort_fragments:
|
||||
if item["extent_count"] == 1:
|
||||
CopyFileFromBytes(item, target_path=r"Z:\test_files")
|
160
files_utils/files_list.py
Normal file
160
files_utils/files_list.py
Normal file
@@ -0,0 +1,160 @@
|
||||
import sqlite3
|
||||
|
||||
|
||||
def GetFilesDBPathInfo(db_path: str = "../src/db_ntfs_info.db",
|
||||
table_name: str = "db_path",
|
||||
files_path=None) -> list:
|
||||
"""
|
||||
根据传入的文件路径列表,在指定表中查询对应记录的 ID 和 Name 字段。
|
||||
|
||||
:param db_path: 数据库文件路径
|
||||
:param table_name: 要查询的数据表名称
|
||||
:param files_path: 文件的完整路径列表
|
||||
:return: 查询结果列表,每项为 {'absolute_path': str, 'id': int, 'name': str}
|
||||
"""
|
||||
if files_path is None:
|
||||
files_path = []
|
||||
results = []
|
||||
|
||||
# 连接数据库
|
||||
conn = sqlite3.connect(db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
for path in files_path:
|
||||
try:
|
||||
# 使用字符串格式化插入表名,参数化查询只适用于值
|
||||
sql = f"SELECT ID, Name FROM {table_name} WHERE Path = ?"
|
||||
cursor.execute(sql, (path,))
|
||||
row = cursor.fetchone()
|
||||
if row:
|
||||
results.append({
|
||||
'absolute_path': path,
|
||||
'id': row[0],
|
||||
'name': row[1]
|
||||
})
|
||||
else:
|
||||
print(f"未找到匹配记录:{path}")
|
||||
except Exception as e:
|
||||
print(f"查询失败:{path},错误:{e}")
|
||||
|
||||
conn.close()
|
||||
return results
|
||||
|
||||
|
||||
def GetFilesDBNodeInfo(db_path: str = "../src/db_ntfs_info.db", table_name: str = "db_node",
|
||||
path_records: list = None) -> list:
|
||||
"""
|
||||
根据 db_path 查询结果中的 ID 去 db_node 表中查找对应的 extent 分片信息。
|
||||
|
||||
:param db_path: 数据库文件路径
|
||||
:param table_name: db_node 表名
|
||||
:param path_records: 来自 get_db_path_info 的结果列表
|
||||
:return: 包含文件分片信息的结果列表
|
||||
"""
|
||||
conn = sqlite3.connect(db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
results = []
|
||||
|
||||
for record in path_records:
|
||||
path_id = record['id']
|
||||
absolute_path = record['absolute_path']
|
||||
name = record['name']
|
||||
|
||||
try:
|
||||
# 查询 db_node 表中 PathID 对应的记录
|
||||
cursor.execute(f"SELECT * FROM {table_name} WHERE PathID = ?", (path_id,))
|
||||
row = cursor.fetchone()
|
||||
|
||||
if not row:
|
||||
print(f"未找到 PathID={path_id} 在表 {table_name} 中的记录")
|
||||
continue
|
||||
|
||||
# 获取字段索引(适用于按列名获取)
|
||||
columns = [desc[0] for desc in cursor.description]
|
||||
|
||||
# 构建字典以便按列名访问
|
||||
node_data = dict(zip(columns, row))
|
||||
|
||||
# 获取 ExtentCount
|
||||
extent_count = node_data.get("ExtentCount", 0)
|
||||
|
||||
# 解析分片信息
|
||||
fragments = []
|
||||
for i in range(1, 5): # extent1 ~ extent4
|
||||
loc = node_data.get(f"extent{i}_Location")
|
||||
length = node_data.get(f"extent{i}_Length")
|
||||
|
||||
if loc is not None and length is not None and length > 0:
|
||||
fragments.append({
|
||||
"start_byte": loc,
|
||||
"length": length
|
||||
})
|
||||
|
||||
results.append({
|
||||
"absolute_path": absolute_path,
|
||||
"name": name,
|
||||
"path_id": path_id,
|
||||
"extent_count": extent_count,
|
||||
"fragments": fragments
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
print(f"查询失败:PathID={path_id}, 错误:{e}")
|
||||
|
||||
conn.close()
|
||||
return results
|
||||
|
||||
|
||||
def SortFragmentsByStartByte(file_extents_list: list) -> list:
|
||||
"""
|
||||
对所有文件的分片按 start_byte 进行排序,并标注是第几个分片。
|
||||
|
||||
:param file_extents_list: get_file_extents_info 返回的结果列表
|
||||
:return: 按 start_byte 排序后的片段列表,包含文件路径、文件名、第几个分片等信息
|
||||
"""
|
||||
all_fragments = []
|
||||
|
||||
for file_info in file_extents_list:
|
||||
absolute_path = file_info['absolute_path']
|
||||
filename = file_info['name']
|
||||
extent_count = file_info['extent_count']
|
||||
fragments = file_info['fragments']
|
||||
|
||||
# 对当前文件的片段排序(虽然通常已经是有序的)
|
||||
sorted_fragments = sorted(fragments, key=lambda x: x['start_byte'])
|
||||
|
||||
# 添加片段索引信息
|
||||
for idx, fragment in enumerate(sorted_fragments, start=1):
|
||||
all_fragments.append({
|
||||
'absolute_path': absolute_path,
|
||||
'filename': filename,
|
||||
'extent_count': extent_count,
|
||||
'start_byte': fragment['start_byte'],
|
||||
'length': fragment['length'],
|
||||
'fragment_index': idx
|
||||
})
|
||||
|
||||
# 全局排序:按 start_byte 排序所有片段
|
||||
all_fragments.sort(key=lambda x: x['start_byte'])
|
||||
|
||||
return all_fragments
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_files = [
|
||||
r"Y:\CloudMusic\AGA - MIZU.mp3",
|
||||
r"Y:\CloudMusic\AGA - 一.mp3",
|
||||
r"Y:\CloudMusic\Aaron Zigman - Main Title.mp3",
|
||||
r"Y:\CloudMusic\Anson Seabra - Keep Your Head Up Princess.mp3",
|
||||
r"Y:\CloudMusic\Anthony Keyrouz,Romy Wave - Something Just Like This (feat. Romy Wave).mp3",
|
||||
r"Y:\CloudMusic\Ava Max - Sweet but Psycho.mp3",
|
||||
r"Y:\CloudMusic\Cecilia Cheung - Turn Into Fireworks and Fall for You.mp3",
|
||||
r"Y:\CloudMusic\Color Music Choir - Something Just Like This (Live).mp3"
|
||||
]
|
||||
path_info = GetFilesDBPathInfo(files_path=test_files)
|
||||
node_info = GetFilesDBNodeInfo(path_records=path_info)
|
||||
result = SortFragmentsByStartByte(node_info)
|
||||
|
||||
for item in result:
|
||||
print(item)
|
73
files_utils/files_save.py
Normal file
73
files_utils/files_save.py
Normal file
@@ -0,0 +1,73 @@
|
||||
import os
|
||||
|
||||
|
||||
def extract_drive_letter(path: str) -> str:
|
||||
"""从绝对路径中提取盘符"""
|
||||
drive = os.path.splitdrive(path)[0]
|
||||
if not drive:
|
||||
raise ValueError(f"无法从路径中提取盘符:{path}")
|
||||
return drive[0].upper() # 返回 'Y'
|
||||
|
||||
|
||||
def CopyFileFromBytes(source_data_dict, target_path):
|
||||
"""
|
||||
根据起始字节和长度,从磁盘中读取数据并保存为目标文件
|
||||
|
||||
:param source_data_dict: 包含源数据信息的字典
|
||||
:param target_path: 目标文件夹路径
|
||||
"""
|
||||
start_byte = source_data_dict.get("start_byte")
|
||||
byte_length = source_data_dict.get("length")
|
||||
absolute_path = source_data_dict.get("absolute_path")
|
||||
file_name = source_data_dict.get("filename")
|
||||
|
||||
if byte_length <= 0:
|
||||
print("错误:字节长度无效")
|
||||
return
|
||||
|
||||
if not absolute_path or not file_name:
|
||||
print("错误:缺少必要的文件信息")
|
||||
return
|
||||
|
||||
source_disk_path = extract_drive_letter(absolute_path)
|
||||
target_file_path = os.path.join(target_path, file_name)
|
||||
|
||||
try:
|
||||
# 创建目标目录(如果不存在)
|
||||
os.makedirs(target_path, exist_ok=True)
|
||||
|
||||
with open(fr"\\.\{source_disk_path}:", 'rb') as disk:
|
||||
disk.seek(start_byte)
|
||||
|
||||
with open(target_file_path, 'wb') as f:
|
||||
remaining = byte_length
|
||||
CHUNK_SIZE = 1024 * 1024 # 1MB
|
||||
while remaining > 0:
|
||||
read_size = min(CHUNK_SIZE, remaining)
|
||||
chunk = disk.read(read_size)
|
||||
if not chunk:
|
||||
print("警告:读取到空数据,可能已到达磁盘末尾。")
|
||||
break
|
||||
f.write(chunk)
|
||||
remaining -= len(chunk)
|
||||
|
||||
print(
|
||||
f"成功:已从字节偏移量 {start_byte} 读取 {byte_length} 字节,保存为 {target_file_path}")
|
||||
|
||||
except PermissionError:
|
||||
print("错误:需要管理员权限访问磁盘设备,请以管理员身份运行此程序")
|
||||
except Exception as e:
|
||||
print(f"发生错误: {str(e)}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_dict = {
|
||||
'absolute_path': 'Y:\\CloudMusic\\Aaron Zigman - Main Title.mp3',
|
||||
'filename': 'Aaron Zigman - Main Title.mp3',
|
||||
'extent_count': 1,
|
||||
'start_byte': 687685632,
|
||||
'length': 7163904,
|
||||
'fragment_index': 1
|
||||
}
|
||||
|
||||
CopyFileFromBytes(test_dict, target_path=r"Z:\RecoveredFiles")
|
37
files_utils/public.py
Normal file
37
files_utils/public.py
Normal file
@@ -0,0 +1,37 @@
|
||||
def ReadDiskBytes(volume_letter: str, start_byte: int, length: int) -> bytes:
|
||||
"""
|
||||
从指定磁盘的指定起始位置读取指定长度的字节。
|
||||
|
||||
:param volume_letter: 盘符(如 "Y")
|
||||
:param start_byte: 起始字节位置(整数)
|
||||
:param length: 要读取的字节数(整数)
|
||||
:return: 读取到的原始字节数据(bytes)
|
||||
"""
|
||||
if not isinstance(volume_letter, str) or len(volume_letter.strip()) != 1:
|
||||
raise ValueError("drive_letter 必须是单个字母,如 'Y'")
|
||||
|
||||
# 构建 Windows 设备路径格式:\\.\Y:
|
||||
disk_path = f"\\\\.\\{volume_letter.strip().upper()}:"
|
||||
|
||||
try:
|
||||
with open(disk_path, "rb") as disk:
|
||||
disk.seek(start_byte)
|
||||
data = disk.read(length)
|
||||
return data
|
||||
except PermissionError:
|
||||
raise PermissionError("权限不足,请以管理员身份运行程序")
|
||||
except Exception as e:
|
||||
raise RuntimeError(f"读取磁盘失败:{e}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
drive = "Y"
|
||||
start = 687685632
|
||||
size = 7163904
|
||||
|
||||
try:
|
||||
content = ReadDiskBytes(drive, start, size)
|
||||
print(f"成功读取 {len(content)} 字节内容。前100字节为:")
|
||||
print(content[:100])
|
||||
except Exception as e:
|
||||
print("错误:", e)
|
@@ -1,47 +0,0 @@
|
||||
def copy_file_from_bytes(start_byte, end_byte, source_disk_path, target_file_path):
|
||||
"""
|
||||
根据起始字节和结束字节偏移量,从磁盘中读取指定范围的数据并保存为目标文件
|
||||
|
||||
参数:
|
||||
start_byte (int): 起始字节偏移量(包含)
|
||||
end_byte (int): 结束字节偏移量(包含)
|
||||
source_disk_path (str): 源磁盘路径(如 r"\\.\Z:")
|
||||
target_file_path (str): 目标文件路径(如 r"E:\demo.jpg")
|
||||
"""
|
||||
if start_byte > end_byte:
|
||||
print("错误:起始字节偏移量不能大于结束字节偏移量")
|
||||
return
|
||||
|
||||
try:
|
||||
with open(source_disk_path, 'rb') as disk:
|
||||
# 计算总字节数
|
||||
total_bytes = end_byte - start_byte + 1
|
||||
|
||||
# 定位到起始位置
|
||||
disk.seek(start_byte)
|
||||
|
||||
# 读取指定范围内的数据
|
||||
file_data = disk.read(total_bytes)
|
||||
|
||||
if not file_data or len(file_data) < total_bytes:
|
||||
print(f"警告:只读取到 {len(file_data)} 字节,未达到预期 {total_bytes} 字节")
|
||||
|
||||
# 写入目标文件
|
||||
with open(target_file_path, 'wb') as f:
|
||||
f.write(file_data)
|
||||
|
||||
print(
|
||||
f"成功:已从字节偏移量 {start_byte} 到 {end_byte} 读取 {len(file_data)} 字节,保存为 {target_file_path}")
|
||||
|
||||
except PermissionError:
|
||||
print("错误:需要管理员权限访问磁盘设备,请以管理员身份运行此程序")
|
||||
except Exception as e:
|
||||
print(f"发生错误: {str(e)}")
|
||||
|
||||
|
||||
copy_file_from_bytes(
|
||||
start_byte=687685632,
|
||||
end_byte=687685632+7163904,
|
||||
source_disk_path=r"\\.\Y:",
|
||||
target_file_path=r"Z:\demo.mp3"
|
||||
)
|
198
test/files_list.py
Normal file
198
test/files_list.py
Normal file
@@ -0,0 +1,198 @@
|
||||
import sqlite3
|
||||
|
||||
|
||||
def GetFilesDBPathInfo(db_path: str = "../src/db_ntfs_info.db",
|
||||
table_name: str = "db_path",
|
||||
files_path=None) -> list:
|
||||
"""
|
||||
根据传入的文件路径列表,在指定表中查询对应记录的 ID 和 Name 字段。
|
||||
|
||||
:param db_path: 数据库文件路径
|
||||
:param table_name: 要查询的数据表名称
|
||||
:param files_path: 文件的完整路径列表
|
||||
:return: 查询结果列表,每项为 {'absolute_path': str, 'id': int, 'name': str}
|
||||
"""
|
||||
if files_path is None:
|
||||
file_paths = []
|
||||
results = []
|
||||
|
||||
# 连接数据库
|
||||
conn = sqlite3.connect(db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
for path in files_path:
|
||||
try:
|
||||
# 使用字符串格式化插入表名,参数化查询只适用于值
|
||||
sql = f"SELECT ID, Name FROM {table_name} WHERE Path = ?"
|
||||
cursor.execute(sql, (path,))
|
||||
row = cursor.fetchone()
|
||||
if row:
|
||||
results.append({
|
||||
'absolute_path': path,
|
||||
'id': row[0],
|
||||
'name': row[1]
|
||||
})
|
||||
else:
|
||||
print(f"未找到匹配记录:{path}")
|
||||
except Exception as e:
|
||||
print(f"查询失败:{path},错误:{e}")
|
||||
|
||||
conn.close()
|
||||
return results
|
||||
|
||||
|
||||
# if __name__ == "__main__":
|
||||
# test_files = [
|
||||
# r"Y:\CloudMusic\AGA - MIZU.mp3",
|
||||
# r"Y:\CloudMusic\AGA - 一.mp3",
|
||||
# r"Y:\CloudMusic\Aaron Zigman - Main Title.mp3",
|
||||
# r"Y:\CloudMusic\Anson Seabra - Keep Your Head Up Princess.mp3",
|
||||
# r"Y:\CloudMusic\Anthony Keyrouz,Romy Wave - Something Just Like This (feat. Romy Wave).mp3",
|
||||
# r"Y:\CloudMusic\Ava Max - Sweet but Psycho.mp3",
|
||||
# r"Y:\CloudMusic\Cecilia Cheung - Turn Into Fireworks and Fall for You.mp3",
|
||||
# r"Y:\CloudMusic\Color Music Choir - Something Just Like This (Live).mp3"
|
||||
# ]
|
||||
#
|
||||
# result = GetFilesDBPathInfo(files_path=test_files)
|
||||
# for item in result:
|
||||
# print(item)
|
||||
|
||||
|
||||
def GetFilesDBNodeInfo(db_path: str = "../src/db_ntfs_info.db", table_name: str = "db_node",
|
||||
path_records: list = None) -> list:
|
||||
"""
|
||||
根据 db_path 查询结果中的 ID 去 db_node 表中查找对应的 extent 分片信息。
|
||||
|
||||
:param db_path: 数据库文件路径
|
||||
:param table_name: db_node 表名
|
||||
:param path_records: 来自 get_db_path_info 的结果列表
|
||||
:return: 包含文件分片信息的结果列表
|
||||
"""
|
||||
conn = sqlite3.connect(db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
results = []
|
||||
|
||||
for record in path_records:
|
||||
path_id = record['id']
|
||||
absolute_path = record['absolute_path']
|
||||
name = record['name']
|
||||
|
||||
try:
|
||||
# 查询 db_node 表中 PathID 对应的记录
|
||||
cursor.execute(f"SELECT * FROM {table_name} WHERE PathID = ?", (path_id,))
|
||||
row = cursor.fetchone()
|
||||
|
||||
if not row:
|
||||
print(f"未找到 PathID={path_id} 在表 {table_name} 中的记录")
|
||||
continue
|
||||
|
||||
# 获取字段索引(适用于按列名获取)
|
||||
columns = [desc[0] for desc in cursor.description]
|
||||
|
||||
# 构建字典以便按列名访问
|
||||
node_data = dict(zip(columns, row))
|
||||
|
||||
# 获取 ExtentCount
|
||||
extent_count = node_data.get("ExtentCount", 0)
|
||||
|
||||
# 解析分片信息
|
||||
fragments = []
|
||||
for i in range(1, 5): # extent1 ~ extent4
|
||||
loc = node_data.get(f"extent{i}_Location")
|
||||
length = node_data.get(f"extent{i}_Length")
|
||||
|
||||
if loc is not None and length is not None and length > 0:
|
||||
fragments.append({
|
||||
"start_byte": loc,
|
||||
"length": length
|
||||
})
|
||||
|
||||
results.append({
|
||||
"absolute_path": absolute_path,
|
||||
"name": name,
|
||||
"path_id": path_id,
|
||||
"extent_count": extent_count,
|
||||
"fragments": fragments
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
print(f"查询失败:PathID={path_id}, 错误:{e}")
|
||||
|
||||
conn.close()
|
||||
return results
|
||||
|
||||
|
||||
# if __name__ == "__main__":
|
||||
# test_files = [
|
||||
# r"Y:\CloudMusic\AGA - MIZU.mp3",
|
||||
# r"Y:\CloudMusic\AGA - 一.mp3",
|
||||
# r"Y:\CloudMusic\Aaron Zigman - Main Title.mp3",
|
||||
# r"Y:\CloudMusic\Anson Seabra - Keep Your Head Up Princess.mp3",
|
||||
# r"Y:\CloudMusic\Anthony Keyrouz,Romy Wave - Something Just Like This (feat. Romy Wave).mp3",
|
||||
# r"Y:\CloudMusic\Ava Max - Sweet but Psycho.mp3",
|
||||
# r"Y:\CloudMusic\Cecilia Cheung - Turn Into Fireworks and Fall for You.mp3",
|
||||
# r"Y:\CloudMusic\Color Music Choir - Something Just Like This (Live).mp3"
|
||||
# ]
|
||||
#
|
||||
# # 第一步:获取 db_path 表中的 ID 和 Name
|
||||
# path_info = GetFilesDBPathInfo(files_path=test_files)
|
||||
#
|
||||
# # 第二步:根据 PathID 查询 db_node 表中的分片信息
|
||||
# file_extents_info = GetFilesDBNodeInfo(path_records=path_info)
|
||||
#
|
||||
# # 打印结果
|
||||
# for item in file_extents_info:
|
||||
# print(item)
|
||||
|
||||
|
||||
def sort_fragments_by_start_byte(file_extents_list: list) -> list:
|
||||
"""
|
||||
对所有文件的分片按 start_byte 进行排序,并标注是第几个分片。
|
||||
|
||||
:param file_extents_list: get_file_extents_info 返回的结果列表
|
||||
:return: 按 start_byte 排序后的片段列表,包含文件路径、文件名、第几个分片等信息
|
||||
"""
|
||||
all_fragments = []
|
||||
|
||||
for file_info in file_extents_list:
|
||||
absolute_path = file_info['absolute_path']
|
||||
filename = file_info['name']
|
||||
fragments = file_info['fragments']
|
||||
|
||||
# 对当前文件的片段排序(虽然通常已经是有序的)
|
||||
sorted_fragments = sorted(fragments, key=lambda x: x['start_byte'])
|
||||
|
||||
# 添加片段索引信息
|
||||
for idx, fragment in enumerate(sorted_fragments, start=1):
|
||||
all_fragments.append({
|
||||
'absolute_path': absolute_path,
|
||||
'filename': filename,
|
||||
'start_byte': fragment['start_byte'],
|
||||
'length': fragment['length'],
|
||||
'fragment_index': idx
|
||||
})
|
||||
|
||||
# 全局排序:按 start_byte 排序所有片段
|
||||
all_fragments.sort(key=lambda x: x['start_byte'])
|
||||
|
||||
return all_fragments
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_files = [
|
||||
r"Y:\CloudMusic\AGA - MIZU.mp3",
|
||||
r"Y:\CloudMusic\AGA - 一.mp3",
|
||||
r"Y:\CloudMusic\Aaron Zigman - Main Title.mp3",
|
||||
r"Y:\CloudMusic\Anson Seabra - Keep Your Head Up Princess.mp3",
|
||||
r"Y:\CloudMusic\Anthony Keyrouz,Romy Wave - Something Just Like This (feat. Romy Wave).mp3",
|
||||
r"Y:\CloudMusic\Ava Max - Sweet but Psycho.mp3",
|
||||
r"Y:\CloudMusic\Cecilia Cheung - Turn Into Fireworks and Fall for You.mp3",
|
||||
r"Y:\CloudMusic\Color Music Choir - Something Just Like This (Live).mp3"
|
||||
]
|
||||
path_info = GetFilesDBPathInfo(files_path=test_files)
|
||||
file_extents_data = GetFilesDBNodeInfo(path_records=path_info)
|
||||
result = sort_fragments_by_start_byte(file_extents_data)
|
||||
|
||||
for item in result:
|
||||
print(item)
|
72
test/files_save.py
Normal file
72
test/files_save.py
Normal file
@@ -0,0 +1,72 @@
|
||||
import os
|
||||
|
||||
|
||||
def extract_drive_letter(path: str) -> str:
|
||||
"""从绝对路径中提取盘符"""
|
||||
drive = os.path.splitdrive(path)[0]
|
||||
if not drive:
|
||||
raise ValueError(f"无法从路径中提取盘符:{path}")
|
||||
return drive[0].upper() # 返回 'Y'
|
||||
|
||||
|
||||
def CopyFileFromBytes(source_data_dict, target_path):
|
||||
"""
|
||||
根据起始字节和长度,从磁盘中读取数据并保存为目标文件
|
||||
|
||||
:param source_data_dict: 包含源数据信息的字典
|
||||
:param target_path: 目标文件夹路径
|
||||
"""
|
||||
start_byte = source_data_dict.get("start_byte")
|
||||
byte_length = source_data_dict.get("length")
|
||||
absolute_path = source_data_dict.get("absolute_path")
|
||||
file_name = source_data_dict.get("filename")
|
||||
|
||||
if byte_length <= 0:
|
||||
print("错误:字节长度无效")
|
||||
return
|
||||
|
||||
if not absolute_path or not file_name:
|
||||
print("错误:缺少必要的文件信息")
|
||||
return
|
||||
|
||||
source_disk_path = extract_drive_letter(absolute_path)
|
||||
target_file_path = os.path.join(target_path, file_name)
|
||||
|
||||
try:
|
||||
# 创建目标目录(如果不存在)
|
||||
os.makedirs(target_path, exist_ok=True)
|
||||
|
||||
with open(fr"\\.\{source_disk_path}:", 'rb') as disk:
|
||||
disk.seek(start_byte)
|
||||
|
||||
with open(target_file_path, 'wb') as f:
|
||||
remaining = byte_length
|
||||
CHUNK_SIZE = 1024 * 1024 # 1MB
|
||||
while remaining > 0:
|
||||
read_size = min(CHUNK_SIZE, remaining)
|
||||
chunk = disk.read(read_size)
|
||||
if not chunk:
|
||||
print("警告:读取到空数据,可能已到达磁盘末尾。")
|
||||
break
|
||||
f.write(chunk)
|
||||
remaining -= len(chunk)
|
||||
|
||||
print(
|
||||
f"成功:已从字节偏移量 {start_byte} 读取 {byte_length} 字节,保存为 {target_file_path}")
|
||||
|
||||
except PermissionError:
|
||||
print("错误:需要管理员权限访问磁盘设备,请以管理员身份运行此程序")
|
||||
except Exception as e:
|
||||
print(f"发生错误: {str(e)}")
|
||||
|
||||
|
||||
test_dict = {
|
||||
'absolute_path': 'Y:\\CloudMusic\\Aaron Zigman - Main Title.mp3',
|
||||
'filename': 'Aaron Zigman - Main Title.mp3',
|
||||
'extent_count': 1,
|
||||
'start_byte': 687685632,
|
||||
'length': 7163904,
|
||||
'fragment_index': 1
|
||||
}
|
||||
|
||||
CopyFileFromBytes(test_dict, target_path=r"Z:\RecoveredFiles")
|
Reference in New Issue
Block a user