$npx -y skills add Impertio-Studio/Frappe_Claude_Skill_Package --skill frappe-core-filesUse when handling file uploads, attachments, private/public file access, or S3 storage configuration. Prevents broken file URLs, permission leaks on private files, and failed uploads from incorrect MIME handling. Covers File DocType, frappe.get_file, upload API, private vs public
| 1 | # Frappe File Management |
| 2 | |
| 3 | ## Quick Reference |
| 4 | |
| 5 | | Action | Method | Notes | |
| 6 | |--------|--------|-------| |
| 7 | | Save file from bytes | `save_file(fname, content, dt, dn)` | Returns File doc | |
| 8 | | Save file from URL | `save_url(file_url, fname, dt, dn)` | Creates File doc from URL | |
| 9 | | Read file content | `frappe.get_file(fname)` | Returns `[filename, content]` | |
| 10 | | Get file path | `get_file_path(file_name)` | Resolves to absolute path | |
| 11 | | Upload via HTTP | `POST /api/method/upload_file` | Multipart form upload | |
| 12 | | Delete file | `frappe.delete_doc("File", name)` | Removes doc + filesystem file | |
| 13 | | Attach print | `frappe.attach_print(dt, dn, print_format)` | Returns `{"fname", "fcontent"}` | |
| 14 | | Get cached doc | `frappe.get_cached_doc("File", name)` | Read-only, cached | |
| 15 | |
| 16 | --- |
| 17 | |
| 18 | ## Decision Tree |
| 19 | |
| 20 | ``` |
| 21 | What file operation do you need? |
| 22 | │ |
| 23 | ├─ Upload a file from user input? |
| 24 | │ ├─ Via web form → Attach field type (auto-handles upload) |
| 25 | │ └─ Via API → POST /api/method/upload_file |
| 26 | │ |
| 27 | ├─ Create a file programmatically? |
| 28 | │ ├─ From bytes/content → save_file(fname, content, dt, dn) |
| 29 | │ ├─ From external URL → save_url(file_url, fname, dt, dn) |
| 30 | │ └─ Full control → frappe.get_doc({"doctype": "File", ...}).insert() |
| 31 | │ |
| 32 | ├─ Read file content? |
| 33 | │ ├─ By filename → frappe.get_file(fname) |
| 34 | │ └─ By File doc → file_doc.get_content() |
| 35 | │ |
| 36 | ├─ Public or private? |
| 37 | │ ├─ Public (anyone with link) → is_private=0, URL: /files/fname |
| 38 | │ └─ Private (permission-based) → is_private=1, URL: /private/files/fname |
| 39 | │ |
| 40 | └─ Generate PDF attachment? |
| 41 | └─ frappe.attach_print(doctype, name, print_format) |
| 42 | ``` |
| 43 | |
| 44 | --- |
| 45 | |
| 46 | ## File DocType: Core Fields |
| 47 | |
| 48 | | Field | Type | Description | |
| 49 | |-------|------|-------------| |
| 50 | | `file_name` | Data | Filename without path | |
| 51 | | `file_url` | Data | URL path (e.g., `/files/report.pdf`) | |
| 52 | | `file_type` | Data | Extension (PDF, PNG, DOCX, etc.) | |
| 53 | | `is_private` | Check | 0 = public, 1 = private | |
| 54 | | `is_folder` | Check | True for folder entries | |
| 55 | | `folder` | Link → File | Parent folder | |
| 56 | | `attached_to_doctype` | Link → DocType | Parent document type | |
| 57 | | `attached_to_name` | Data | Parent document name | |
| 58 | | `attached_to_field` | Data | Field name on parent | |
| 59 | | `content_hash` | Data | SHA-256 for deduplication | |
| 60 | | `file_size` | Int | Size in bytes | |
| 61 | |
| 62 | --- |
| 63 | |
| 64 | ## File URL Patterns |
| 65 | |
| 66 | | Type | URL Pattern | Filesystem Path | |
| 67 | |------|------------|-----------------| |
| 68 | | Public | `/files/{filename}` | `{site}/public/files/{filename}` | |
| 69 | | Private | `/private/files/{filename}` | `{site}/private/files/{filename}` | |
| 70 | | Remote | `https://...` | Not stored locally | |
| 71 | | API | `/api/method/{path}` | Generated dynamically | |
| 72 | |
| 73 | Valid URL prefixes: `http://`, `https://`, `/api/method/`, `/files/`, `/private/files/`. |
| 74 | |
| 75 | ALWAYS use `/private/files/` for sensitive documents. Public files are accessible to anyone with the URL, including unauthenticated users. |
| 76 | |
| 77 | --- |
| 78 | |
| 79 | ## Permission Model |
| 80 | |
| 81 | Frappe files use a three-tier permission model: |
| 82 | |
| 83 | 1. **Administrator** — unrestricted access to all files |
| 84 | 2. **Public files** (`is_private=0`) — readable by anyone with the URL (no authentication required for read) |
| 85 | 3. **Private files** (`is_private=1`) — access requires: |
| 86 | - User is the file owner, OR |
| 87 | - User has explicit share on the file, OR |
| 88 | - User has read permission on the `attached_to_doctype`/`attached_to_name` document |
| 89 | |
| 90 | NEVER store sensitive data as public files. ALWAYS set `is_private=1` for documents containing personal data, financial records, or confidential information. |
| 91 | |
| 92 | --- |
| 93 | |
| 94 | ## Programmatic File Operations |
| 95 | |
| 96 | ### Save File from Content |
| 97 | |
| 98 | ```python |
| 99 | from frappe.utils.file_manager import save_file |
| 100 | |
| 101 | # Save a generated CSV |
| 102 | csv_content = "Name,Amount\nACME,1000\nGlobex,2000" |
| 103 | file_doc = save_file( |
| 104 | fname="report.csv", |
| 105 | content=csv_content.encode("utf-8"), |
| 106 | dt="Sales Invoice", # attach to this DocType |
| 107 | dn="SINV-00001", # attach to this document |
| 108 | folder="Home/Attachments", # optional folder |
| 109 | is_private=1, # private file |
| 110 | ) |
| 111 | # file_doc.file_url → "/private/files/report.csv" |
| 112 | ``` |
| 113 | |
| 114 | ### Save File from URL |
| 115 | |
| 116 | ```python |
| 117 | from frappe.utils.file_manager import save_url |
| 118 | |
| 119 | file_doc = save_url( |
| 120 | file_url="https://example.com/logo.png", |
| 121 | filename="company-logo.png", |
| 122 | dt="Company", |
| 123 | dn="My Company", |
| 124 | folder="Hom |