dfs-sync 1.0.2__tar.gz → 1.0.3__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.3}/PKG-INFO +1 -1
  2. {dfs_sync-1.0.2 → dfs_sync-1.0.3}/dfs/__init__.py +1 -1
  3. {dfs_sync-1.0.2 → dfs_sync-1.0.3}/dfs/core/transfer_manager.py +8 -6
  4. {dfs_sync-1.0.2 → dfs_sync-1.0.3}/dfs/utils/path_handler.py +51 -0
  5. {dfs_sync-1.0.2 → dfs_sync-1.0.3/dfs_sync.egg-info}/PKG-INFO +1 -1
  6. {dfs_sync-1.0.2 → dfs_sync-1.0.3}/LICENSE +0 -0
  7. {dfs_sync-1.0.2 → dfs_sync-1.0.3}/MANIFEST.in +0 -0
  8. {dfs_sync-1.0.2 → dfs_sync-1.0.3}/README.md +0 -0
  9. {dfs_sync-1.0.2 → dfs_sync-1.0.3}/dfs/cli/__init__.py +0 -0
  10. {dfs_sync-1.0.2 → dfs_sync-1.0.3}/dfs/cli/main.py +0 -0
  11. {dfs_sync-1.0.2 → dfs_sync-1.0.3}/dfs/core/__init__.py +0 -0
  12. {dfs_sync-1.0.2 → dfs_sync-1.0.3}/dfs/core/config.py +0 -0
  13. {dfs_sync-1.0.2 → dfs_sync-1.0.3}/dfs/core/logger.py +0 -0
  14. {dfs_sync-1.0.2 → dfs_sync-1.0.3}/dfs/protocols/__init__.py +0 -0
  15. {dfs_sync-1.0.2 → dfs_sync-1.0.3}/dfs/protocols/base.py +0 -0
  16. {dfs_sync-1.0.2 → dfs_sync-1.0.3}/dfs/protocols/sftp_client.py +0 -0
  17. {dfs_sync-1.0.2 → dfs_sync-1.0.3}/dfs/utils/__init__.py +0 -0
  18. {dfs_sync-1.0.2 → dfs_sync-1.0.3}/dfs/utils/file_filter.py +0 -0
  19. {dfs_sync-1.0.2 → dfs_sync-1.0.3}/dfs/utils/file_info_cache.py +0 -0
  20. {dfs_sync-1.0.2 → dfs_sync-1.0.3}/dfs/utils/progress_display.py +0 -0
  21. {dfs_sync-1.0.2 → dfs_sync-1.0.3}/dfs_sync.egg-info/SOURCES.txt +0 -0
  22. {dfs_sync-1.0.2 → dfs_sync-1.0.3}/dfs_sync.egg-info/dependency_links.txt +0 -0
  23. {dfs_sync-1.0.2 → dfs_sync-1.0.3}/dfs_sync.egg-info/entry_points.txt +0 -0
  24. {dfs_sync-1.0.2 → dfs_sync-1.0.3}/dfs_sync.egg-info/requires.txt +0 -0
  25. {dfs_sync-1.0.2 → dfs_sync-1.0.3}/dfs_sync.egg-info/top_level.txt +0 -0
  26. {dfs_sync-1.0.2 → dfs_sync-1.0.3}/pyproject.toml +0 -0
  27. {dfs_sync-1.0.2 → dfs_sync-1.0.3}/requirements.txt +0 -0
  28. {dfs_sync-1.0.2 → dfs_sync-1.0.3}/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.3
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.3"
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
  获取路径类型
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dfs-sync
3
- Version: 1.0.2
3
+ Version: 1.0.3
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