Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

move.py 2.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. """
  2. Move a file in the safest way possible::
  3. >>> from django.core.files.move import file_move_safe
  4. >>> file_move_safe("/tmp/old_file", "/tmp/new_file")
  5. """
  6. import errno
  7. import os
  8. from shutil import copystat
  9. from django.core.files import locks
  10. __all__ = ['file_move_safe']
  11. def _samefile(src, dst):
  12. # Macintosh, Unix.
  13. if hasattr(os.path, 'samefile'):
  14. try:
  15. return os.path.samefile(src, dst)
  16. except OSError:
  17. return False
  18. # All other platforms: check for same pathname.
  19. return (os.path.normcase(os.path.abspath(src)) ==
  20. os.path.normcase(os.path.abspath(dst)))
  21. def file_move_safe(old_file_name, new_file_name, chunk_size=1024 * 64, allow_overwrite=False):
  22. """
  23. Move a file from one location to another in the safest way possible.
  24. First, try ``os.rename``, which is simple but will break across filesystems.
  25. If that fails, stream manually from one file to another in pure Python.
  26. If the destination file exists and ``allow_overwrite`` is ``False``, raise
  27. ``IOError``.
  28. """
  29. # There's no reason to move if we don't have to.
  30. if _samefile(old_file_name, new_file_name):
  31. return
  32. try:
  33. if not allow_overwrite and os.access(new_file_name, os.F_OK):
  34. raise IOError("Destination file %s exists and allow_overwrite is False" % new_file_name)
  35. os.rename(old_file_name, new_file_name)
  36. return
  37. except OSError:
  38. # OSError happens with os.rename() if moving to another filesystem or
  39. # when moving opened files on certain operating systems.
  40. pass
  41. # first open the old file, so that it won't go away
  42. with open(old_file_name, 'rb') as old_file:
  43. # now open the new file, not forgetting allow_overwrite
  44. fd = os.open(new_file_name, (os.O_WRONLY | os.O_CREAT | getattr(os, 'O_BINARY', 0) |
  45. (os.O_EXCL if not allow_overwrite else 0)))
  46. try:
  47. locks.lock(fd, locks.LOCK_EX)
  48. current_chunk = None
  49. while current_chunk != b'':
  50. current_chunk = old_file.read(chunk_size)
  51. os.write(fd, current_chunk)
  52. finally:
  53. locks.unlock(fd)
  54. os.close(fd)
  55. try:
  56. copystat(old_file_name, new_file_name)
  57. except PermissionError as e:
  58. # Certain filesystems (e.g. CIFS) fail to copy the file's metadata if
  59. # the type of the destination filesystem isn't the same as the source
  60. # filesystem; ignore that.
  61. if e.errno != errno.EPERM:
  62. raise
  63. try:
  64. os.remove(old_file_name)
  65. except PermissionError as e:
  66. # Certain operating systems (Cygwin and Windows)
  67. # fail when deleting opened files, ignore it. (For the
  68. # systems where this happens, temporary files will be auto-deleted
  69. # on close anyway.)
  70. if getattr(e, 'winerror', 0) != 32:
  71. raise