dfs-sync 1.0.2__tar.gz → 1.0.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. {dfs_sync-1.0.2/dfs_sync.egg-info → dfs_sync-1.0.4}/PKG-INFO +1 -1
  2. {dfs_sync-1.0.2 → dfs_sync-1.0.4}/dfs/__init__.py +1 -1
  3. {dfs_sync-1.0.2 → dfs_sync-1.0.4}/dfs/core/transfer_manager.py +8 -6
  4. {dfs_sync-1.0.2 → dfs_sync-1.0.4}/dfs/utils/path_handler.py +51 -0
  5. {dfs_sync-1.0.2 → dfs_sync-1.0.4}/dfs/utils/progress_display.py +17 -8
  6. {dfs_sync-1.0.2 → dfs_sync-1.0.4/dfs_sync.egg-info}/PKG-INFO +1 -1
  7. {dfs_sync-1.0.2 → dfs_sync-1.0.4}/LICENSE +0 -0
  8. {dfs_sync-1.0.2 → dfs_sync-1.0.4}/MANIFEST.in +0 -0
  9. {dfs_sync-1.0.2 → dfs_sync-1.0.4}/README.md +0 -0
  10. {dfs_sync-1.0.2 → dfs_sync-1.0.4}/dfs/cli/__init__.py +0 -0
  11. {dfs_sync-1.0.2 → dfs_sync-1.0.4}/dfs/cli/main.py +0 -0
  12. {dfs_sync-1.0.2 → dfs_sync-1.0.4}/dfs/core/__init__.py +0 -0
  13. {dfs_sync-1.0.2 → dfs_sync-1.0.4}/dfs/core/config.py +0 -0
  14. {dfs_sync-1.0.2 → dfs_sync-1.0.4}/dfs/core/logger.py +0 -0
  15. {dfs_sync-1.0.2 → dfs_sync-1.0.4}/dfs/protocols/__init__.py +0 -0
  16. {dfs_sync-1.0.2 → dfs_sync-1.0.4}/dfs/protocols/base.py +0 -0
  17. {dfs_sync-1.0.2 → dfs_sync-1.0.4}/dfs/protocols/sftp_client.py +0 -0
  18. {dfs_sync-1.0.2 → dfs_sync-1.0.4}/dfs/utils/__init__.py +0 -0
  19. {dfs_sync-1.0.2 → dfs_sync-1.0.4}/dfs/utils/file_filter.py +0 -0
  20. {dfs_sync-1.0.2 → dfs_sync-1.0.4}/dfs/utils/file_info_cache.py +0 -0
  21. {dfs_sync-1.0.2 → dfs_sync-1.0.4}/dfs_sync.egg-info/SOURCES.txt +0 -0
  22. {dfs_sync-1.0.2 → dfs_sync-1.0.4}/dfs_sync.egg-info/dependency_links.txt +0 -0
  23. {dfs_sync-1.0.2 → dfs_sync-1.0.4}/dfs_sync.egg-info/entry_points.txt +0 -0
  24. {dfs_sync-1.0.2 → dfs_sync-1.0.4}/dfs_sync.egg-info/requires.txt +0 -0
  25. {dfs_sync-1.0.2 → dfs_sync-1.0.4}/dfs_sync.egg-info/top_level.txt +0 -0
  26. {dfs_sync-1.0.2 → dfs_sync-1.0.4}/pyproject.toml +0 -0
  27. {dfs_sync-1.0.2 → dfs_sync-1.0.4}/requirements.txt +0 -0
  28. {dfs_sync-1.0.2 → dfs_sync-1.0.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dfs-sync
3
- Version: 1.0.2
3
+ Version: 1.0.4
4
4
  Summary: 文件传输工具
5
5
  Author-email: xigua <2587125111@qq.com>
6
6
  License: MIT
@@ -5,7 +5,7 @@ DFS - 分布式文件同步工具
5
5
  支持协议: SFTP
6
6
  """
7
7
 
8
- __version__ = "1.0.2"
8
+ __version__ = "1.0.4"
9
9
  __author__ = "xigua"
10
10
  __description__ = "分布式文件同步工具 - 高性能并发文件传输"
11
11
 
@@ -365,9 +365,10 @@ class TransferManager:
365
365
  if file_info.is_directory:
366
366
  continue
367
367
 
368
- # 计算本地文件路径
369
- rel_path = os.path.relpath(file_info.path, remote_path)
370
- local_file_path = self.path_handler.join_paths(local_path, rel_path)
368
+ # 计算本地文件路径(Windows 下自动净化非法字符)
369
+ local_file_path = self.path_handler.remote_to_local_path(
370
+ file_info.path, remote_path, local_path
371
+ )
371
372
 
372
373
  # 应用文件过滤器
373
374
  if self.file_filter.should_ignore(file_info.path, remote_path):
@@ -437,9 +438,10 @@ class TransferManager:
437
438
  if not file_info or file_info.is_directory:
438
439
  continue
439
440
 
440
- # 计算本地文件路径
441
- rel_path = os.path.relpath(file_path, remote_path)
442
- local_file_path = self.path_handler.join_paths(local_path, rel_path)
441
+ # 计算本地文件路径(Windows 下自动净化非法字符)
442
+ local_file_path = self.path_handler.remote_to_local_path(
443
+ file_path, remote_path, local_path
444
+ )
443
445
 
444
446
  # 直接创建任务(使用已获取的基础信息)
445
447
  await self._add_file_task_with_basic_info(
@@ -411,6 +411,57 @@ class PathHandler:
411
411
  """
412
412
  return os.path.expanduser(path)
413
413
 
414
+ @staticmethod
415
+ def sanitize_windows_filename(name: str) -> str:
416
+ """
417
+ 将文件名组件中 Windows 不支持的字符替换为百分号编码形式。
418
+ Windows 文件名非法字符(不含路径分隔符): : * ? " < > |
419
+ """
420
+ replacements = {
421
+ ':': '%3A',
422
+ '*': '%2A',
423
+ '?': '%3F',
424
+ '"': '%22',
425
+ '<': '%3C',
426
+ '>': '%3E',
427
+ '|': '%7C',
428
+ }
429
+ for char, encoded in replacements.items():
430
+ name = name.replace(char, encoded)
431
+ return name
432
+
433
+ def remote_to_local_path(self, remote_file_path: str, remote_base: str, local_base: str) -> str:
434
+ """
435
+ 将远程文件路径转换为本地路径,在 Windows 上自动净化非法字符。
436
+
437
+ Args:
438
+ remote_file_path: 远程文件完整路径(使用正斜杠)
439
+ remote_base: 远程基础目录路径
440
+ local_base: 本地基础目录路径
441
+
442
+ Returns:
443
+ 适合当前操作系统的本地文件路径
444
+ """
445
+ remote_file_norm = remote_file_path.replace('\\', '/')
446
+ remote_base_norm = remote_base.replace('\\', '/')
447
+ if not remote_base_norm.endswith('/'):
448
+ remote_base_norm += '/'
449
+
450
+ if remote_file_norm.startswith(remote_base_norm):
451
+ rel_str = remote_file_norm[len(remote_base_norm):]
452
+ else:
453
+ rel_str = os.path.relpath(remote_file_path, remote_base).replace('\\', '/')
454
+
455
+ components = [c for c in rel_str.split('/') if c]
456
+ if not components:
457
+ return local_base
458
+
459
+ if platform.system() == 'Windows':
460
+ components = [self.sanitize_windows_filename(c) for c in components]
461
+
462
+ local_rel = os.path.join(*components)
463
+ return os.path.join(local_base, local_rel)
464
+
414
465
  def get_path_type(self, path: str) -> str:
415
466
  """
416
467
  获取路径类型
@@ -9,13 +9,22 @@
9
9
  """
10
10
 
11
11
  import os
12
- import sys
13
12
  import time
14
13
  import threading
15
14
  from typing import Dict, Optional
16
15
  from ..protocols.base import TransferProgress, TransferStatus
17
16
 
18
17
 
18
+ _ICONS = {
19
+ 'upload': '↑ ',
20
+ 'download': '↓ ',
21
+ 'skip': '» ',
22
+ 'ok': '✓ ',
23
+ 'fail': '✗ ',
24
+ 'other': '- ',
25
+ }
26
+
27
+
19
28
  class SimpleProgressDisplay:
20
29
  """简化的进度显示器"""
21
30
 
@@ -207,19 +216,19 @@ class SimpleProgressDisplay:
207
216
  Returns:
208
217
  格式化的进度行字符串
209
218
  """
210
- # 状态图标(统一在此处配置)
219
+ # 状态图标
211
220
  if progress.status == TransferStatus.UPLOADING:
212
- icon = "⬆️ "
221
+ icon = _ICONS['upload']
213
222
  elif progress.status == TransferStatus.DOWNLOADING:
214
- icon = "⬇️ "
223
+ icon = _ICONS['download']
215
224
  elif progress.status == TransferStatus.COMPLETED and progress.is_skipped:
216
- icon = "⏭️ "
225
+ icon = _ICONS['skip']
217
226
  elif progress.status == TransferStatus.COMPLETED:
218
- icon = "✅"
227
+ icon = _ICONS['ok']
219
228
  elif progress.status == TransferStatus.FAILED:
220
- icon = "❌"
229
+ icon = _ICONS['fail']
221
230
  else:
222
- icon = "📁"
231
+ icon = _ICONS['other']
223
232
 
224
233
  # 分配任务序号(首次出现时);调用方已持有 _lock
225
234
  file_path = progress.file_path
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dfs-sync
3
- Version: 1.0.2
3
+ Version: 1.0.4
4
4
  Summary: 文件传输工具
5
5
  Author-email: xigua <2587125111@qq.com>
6
6
  License: MIT
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes