This addon defines a new field type FSFile which is a file field that stores a file in an external filesystem instead of the odoo's filestore. This is useful for large files that you don't want to store in the filestore. Moreover, the field value provides you an interface to access the file's contents and metadata.
auto_awesome What this module does
AI-generated from the code · Odoo 18.0Adds a new field type that stores files in an external filesystem instead of Odoo's internal filestore, with an upload/download/delete widget in forms. Useful for large files.
This module provides a new kind of file field for Odoo developers. Instead of keeping file contents inside Odoo's own file storage, the file is saved in an external filesystem storage, which is better suited to large files.
On screen, the field appears as a file widget: when empty it shows an "Upload your file" button; once a file is present it shows the file name as a download link, plus small pencil (edit/replace) and trash (clear) buttons. Files can be uploaded, replaced and removed directly from the form.
Behind the scenes, each file is linked to an ir.attachment record, and the field exposes useful information such as the file name, size, type (mimetype) and a URL to access it. Reading the field always returns this information rather than raw content.
Business rules and automatic behaviours: the field only accepts bytes, a dictionary with filename and base64 content, a file-like object, or an existing file value; otherwise it raises an error. When a file is written, an attachment is created or updated automatically; when the value is cleared, the related attachment is deleted. The file name can be forced through the fs_filename context key when uploading raw bytes.
It touches the ir.attachment model and adds the fs_file field type to the web client. No menus or reports are added.
Depends on
Required by other modules
Text generated by AI (DeepSeek) from reading the Odoo 18.0 code on GitHub; it may contain mistakes, so check the code before deciding. View the code on GitHub
Availability by version
Reviewed: 2026-11-03Confirmation status: 3 confirmed, 6 with no information of 9 versions.
Sources · Odoo 18.0
Confirmed on 2026-11-03 in branch 18.0 of OCA/storage.
terminal Installation and usage
OCA documentation · Odoo 18.0Usage
Source · Odoo 18.0The new field FSFile has been developed to allows you to store files in an external filesystem storage. Its design is based on the following principles:
- The content of the file must be read from the filesystem only when needed.
- It must be possible to manipulate the file content as a stream by default.
- Unlike Odoo's Binary field, the content is the raw file content by default (no base64 encoding).
- To allows to exchange the file content with other systems, writing the content as base64 is possible. The read operation will return a json structure with the filename, the mimetype, the size and a url to download the file.
This design allows to minimize the memory consumption of the server when manipulating large files and exchanging them with other systems through the default jsonrpc interface.
Concretely, this design allows you to write code like this:
from IO import BytesIO
from odoo import models, fields
from odoo.addons.fs_file.fields import FSFile
class MyModel(models.Model):
_name = 'my.model'
name = fields.Char()
file = FSFile()
# Create a new record with a raw content
my_model = MyModel.create({
'name': 'My File',
'file': BytesIO(b"content"),
})
assert(my_model.file.read() == b"content")
# Create a new record with a base64 encoded content
my_model = MyModel.create({
'name': 'My File',
'file': b"content".encode('base64'),
})
assert(my_model.file.read() == b"content")
# Create a new record with a file content
my_model = MyModel.create({
'name': 'My File',
'file': open('my_file.txt', 'rb'),
})
assert(my_model.file.read() == b"content")
assert(my_model.file.name == "my_file.txt")
# create a record with a file content as base64 encoded and a filename
# This method is useful to create a record from a file uploaded
# through the web interface.
my_model = MyModel.create({
'name': 'My File',
'file': {
'filename': 'my_file.txt',
'content': base64.b64encode(b"content"),
},
})
assert(my_model.file.read() == b"content")
assert(my_model.file.name == "my_file.txt")
# write the content of the file as base64 encoded and a filename
# This method is useful to update a record from a file uploaded
# through the web interface.
my_model.write({
'file': {
'name': 'my_file.txt',
'file': base64.b64encode(b"content"),
},
})
# the call to read() will return a json structure with the filename,
# the mimetype, the size and a url to download the file.
info = my_model.file.read()
assert(info["file"] == {
"filename": "my_file.txt",
"mimetype": "text/plain",
"size": 7,
"url": "/web/content/1234/my_file.txt",
})
# use the field as a file stream
# In such a case, the content is read from the filesystem without being
# stored in memory.
with my_model.file.open("rb) as f:
assert(f.read() == b"content")
# use the field as a file stream to write the content
# In such a case, the content is written to the filesystem without being
# stored in memory. This kind of approach is useful to manipulate large
# files and to avoid to use too much memory.
# Transactional behaviour is ensured by the implementation!
with my_model.file.open("wb") as f:
f.write(b"content")No verified information in OCA: Installation, Configuration.
Installation instructions, the distribution package, dependencies and configuration steps are not shown because they have not yet been verified in the module documentation for any version.
Data imported from GitHub: this sheet reflects the latest catalog review, not a live lookup. Always check the module documentation and staging before installing in production.