Frappe_Claude_Skill_Package frappe-core-files
install
source · Clone the upstream repo
git clone https://github.com/OpenAEC-Foundation/Frappe_Claude_Skill_Package
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/OpenAEC-Foundation/Frappe_Claude_Skill_Package "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/source/core/frappe-core-files" ~/.claude/skills/openaec-foundation-frappe-claude-skill-package-frappe-core-files && rm -rf "$T"
manifest:
skills/source/core/frappe-core-files/SKILL.mdsource content
Frappe File Management
Quick Reference
| Action | Method | Notes |
|---|---|---|
| Save file from bytes | | Returns File doc |
| Save file from URL | | Creates File doc from URL |
| Read file content | | Returns |
| Get file path | | Resolves to absolute path |
| Upload via HTTP | | Multipart form upload |
| Delete file | | Removes doc + filesystem file |
| Attach print | | Returns |
| Get cached doc | | Read-only, cached |
Decision Tree
What file operation do you need? │ ├─ Upload a file from user input? │ ├─ Via web form → Attach field type (auto-handles upload) │ └─ Via API → POST /api/method/upload_file │ ├─ Create a file programmatically? │ ├─ From bytes/content → save_file(fname, content, dt, dn) │ ├─ From external URL → save_url(file_url, fname, dt, dn) │ └─ Full control → frappe.get_doc({"doctype": "File", ...}).insert() │ ├─ Read file content? │ ├─ By filename → frappe.get_file(fname) │ └─ By File doc → file_doc.get_content() │ ├─ Public or private? │ ├─ Public (anyone with link) → is_private=0, URL: /files/fname │ └─ Private (permission-based) → is_private=1, URL: /private/files/fname │ └─ Generate PDF attachment? └─ frappe.attach_print(doctype, name, print_format)
File DocType: Core Fields
| Field | Type | Description |
|---|---|---|
| Data | Filename without path |
| Data | URL path (e.g., ) |
| Data | Extension (PDF, PNG, DOCX, etc.) |
| Check | 0 = public, 1 = private |
| Check | True for folder entries |
| Link → File | Parent folder |
| Link → DocType | Parent document type |
| Data | Parent document name |
| Data | Field name on parent |
| Data | SHA-256 for deduplication |
| Int | Size in bytes |
File URL Patterns
| Type | URL Pattern | Filesystem Path |
|---|---|---|
| Public | | |
| Private | | |
| Remote | | Not stored locally |
| API | | Generated dynamically |
Valid URL prefixes:
http://, https://, /api/method/, /files/, /private/files/.
ALWAYS use
/private/files/ for sensitive documents. Public files are accessible to anyone with the URL, including unauthenticated users.
Permission Model
Frappe files use a three-tier permission model:
- Administrator — unrestricted access to all files
- Public files (
) — readable by anyone with the URL (no authentication required for read)is_private=0 - Private files (
) — access requires:is_private=1- User is the file owner, OR
- User has explicit share on the file, OR
- User has read permission on the
/attached_to_doctype
documentattached_to_name
NEVER store sensitive data as public files. ALWAYS set
is_private=1 for documents containing personal data, financial records, or confidential information.
Programmatic File Operations
Save File from Content
from frappe.utils.file_manager import save_file # Save a generated CSV csv_content = "Name,Amount\nACME,1000\nGlobex,2000" file_doc = save_file( fname="report.csv", content=csv_content.encode("utf-8"), dt="Sales Invoice", # attach to this DocType dn="SINV-00001", # attach to this document folder="Home/Attachments", # optional folder is_private=1, # private file ) # file_doc.file_url → "/private/files/report.csv"
Save File from URL
from frappe.utils.file_manager import save_url file_doc = save_url( file_url="https://example.com/logo.png", filename="company-logo.png", dt="Company", dn="My Company", folder="Home", is_private=0, )
Read File Content
# By filename filename, content = frappe.get_file("report.csv") # By File document file_doc = frappe.get_doc("File", {"file_name": "report.csv"}) content_bytes = file_doc.get_content()
Create File Document Directly
file_doc = frappe.get_doc({ "doctype": "File", "file_name": "generated-report.pdf", "attached_to_doctype": "Sales Invoice", "attached_to_name": "SINV-00001", "is_private": 1, "content": pdf_bytes, # raw bytes — written to disk on insert }).insert(ignore_permissions=True)
Generate and Attach PDF
# Create PDF attachment dict (for use with sendmail) pdf_attachment = frappe.attach_print( "Sales Invoice", "SINV-00001", print_format="Standard", ) # Returns: {"fname": "Sales Invoice - SINV-00001.pdf", "fcontent": <bytes>} # Save PDF as file attachment from frappe.utils.file_manager import save_file pdf = frappe.get_print("Sales Invoice", "SINV-00001", print_format="Standard", as_pdf=True) save_file("invoice.pdf", pdf, "Sales Invoice", "SINV-00001", is_private=1)
File Upload via REST API
# Upload file attached to a document curl -X POST https://site.example.com/api/method/upload_file \ -H "Authorization: token api_key:api_secret" \ -F "file=@/path/to/document.pdf" \ -F "doctype=Sales Invoice" \ -F "docname=SINV-00001" \ -F "is_private=1"
Response:
{ "message": { "name": "FILE-00001", "file_name": "document.pdf", "file_url": "/private/files/document.pdf", "is_private": 1 } }
File Size and Extension Limits
Default max file size: 10 MB per attachment.
Override in
site_config.json:
{ "max_file_size": 20971520 }
Max attachments per document: Set via Customize Form → Max Attachments field on the DocType.
Check file size programmatically:
from frappe.utils.file_manager import check_max_file_size check_max_file_size(content) # raises MaxFileSizeReachedError if too large
Attach Field Types
| Field Type | Stores | UI |
|---|---|---|
| Single file URL | File picker + upload button |
| Single image URL | Image preview + upload |
Both store the
file_url string in the field value. The File DocType record is created separately with attached_to_field set.
S3 / Cloud Storage Integration
Frappe supports custom file storage via the
delete_file_data_content hook and custom upload handlers.
S3 via frappe-s3-attachment or similar app
# In hooks.py of custom app delete_file_data_content = "my_app.storage.delete_from_s3"
ALWAYS test file deletion when using custom storage backends — the default
delete_file_from_filesystem only handles local files.
Configuration Pattern
# site_config.json for S3-compatible storage { "s3_bucket": "my-frappe-files", "s3_region": "eu-west-1", "s3_access_key": "AKIA...", "s3_secret_key": "...", }
Version Differences
| Feature | v14 | v15 | v16 |
|---|---|---|---|
| File DocType | Available | Available | Available |
dedup | Available | Available | Available |
| Image optimization | Manual | Auto (1920x1080, 85%) | Auto |
| Import/Export Zip | Not available | Available | Available |
See Also
- references/examples.md — File operation code examples
- references/anti-patterns.md — Common file handling mistakes
— Permission model for file accessfrappe-core-permissions
— Database operations for File queriesfrappe-core-database