diff --git a/frappe/core/doctype/file/file.py b/frappe/core/doctype/file/file.py index 3abf1d0c87..d7b4b8e247 100755 --- a/frappe/core/doctype/file/file.py +++ b/frappe/core/doctype/file/file.py @@ -738,6 +738,7 @@ class File(Document): name=self.file_name, suffix=self.content_hash[-6:], is_private=self.is_private, + content_hash=self.content_hash, ) call_hook_method("before_write_file", file_size=self.file_size) write_file_method = get_hook_method("write_file") diff --git a/frappe/core/doctype/file/utils.py b/frappe/core/doctype/file/utils.py index f08ca4cd19..67abd82879 100644 --- a/frappe/core/doctype/file/utils.py +++ b/frappe/core/doctype/file/utils.py @@ -172,7 +172,12 @@ def delete_file(path: str) -> None: def remove_file_by_url(file_url: str, doctype: str | None = None, name: str | None = None) -> "Document": if doctype and name: fid = frappe.db.get_value( - "File", {"file_url": file_url, "attached_to_doctype": doctype, "attached_to_name": name} + "File", + { + "file_url": file_url, + "attached_to_doctype": doctype, + "attached_to_name": name, + }, ) else: fid = frappe.db.get_value("File", {"file_url": file_url}) @@ -189,20 +194,28 @@ def get_content_hash(content: bytes | str) -> str: return hashlib.md5(content, usedforsecurity=False).hexdigest() # nosec -def generate_file_name(name: str, suffix: str | None = None, is_private: bool = False) -> str: +def generate_file_name( + name: str, suffix: str | None = None, is_private: bool = False, content_hash=None +) -> str: """Generate conflict-free file name. Suffix will be ignored if name available. If the provided suffix doesn't result in an available path, a random suffix will be picked. """ - def path_exists(name, is_private): - return os.path.exists(encode(get_files_path(name, is_private=is_private))) + def different_file_exists_at_path(name, is_private): + path = encode(get_files_path(name, is_private=is_private)) + if not os.path.exists(path): + return False + if content_hash: + with open(path, "rb") as f: + return get_content_hash(f.read()) != content_hash + return True - if not path_exists(name, is_private): + if not different_file_exists_at_path(name, is_private): return name candidate_path = get_file_name(name, suffix) - if path_exists(candidate_path, is_private): + if different_file_exists_at_path(candidate_path, is_private): return generate_file_name(name, is_private=is_private) return candidate_path