Repository
OCA/storage · module folder · Try on Runboat
Module version
1.1.2
Category
Knowledge Management
Folder size
0.34 MB
License
AGPL-3
Application
No
Auto-installable
No
Website
https://github.com/OCA/storage
Last tracking update
2026-08-07 09:06:26
Authors
Camptocamp, ACSONE SA/NV, Odoo Community Association (OCA)
Maintainers
Camptocamp, ACSONE SA/NV, Odoo Community Association (OCA)
Committers
Stéphane Bidoul, Laurent Mignon (ACSONE), Iván Todorovich, Weblate, OCA-git-bot, oca-ci
Odoo dependencies
OCA/server-env:
OCA/storage:
odoo/odoo:
Python dependencies
fsspec>=2025.3.0, python_slugify, fsspec>=2024.5.0
System dependencies
None
Required by
fs_attachment_s3, fs_file
Description
In some cases, you need to store attachment in another system that the
Odoo's filestore. For example, when your deployment is based on a
multi-server architecture to ensure redundancy and scalability, your
attachments must be stored in a way that they are accessible from all
the servers. In this way, you can use a shared storage system like NFS
or a cloud storage like S3 compliant storage, or....

This addon extend the storage mechanism of Odoo's attachments to allow
you to store them in any storage filesystem supported by the Python
library [fsspec](https://filesystem-spec.readthedocs.io/en/latest/) and
made available via the fs_storage addon.

In contrast to Odoo, when a file is stored into an external storage,
this addon ensures that the filename keeps its meaning (In odoo the
filename into the filestore is the file content checksum). Concretely
the filename is based on the pattern:
'\<name-without-extension\>-\<attachment-id\>-\<version\>.\<extension\>'

This addon also adds on the attachments 2 new fields to use to retrieve
the file content from a URL:

- `Internal URL`: URL to retrieve the file content from the Odoo's
  filestore.
- `Filesystem URL`: URL to retrieve the file content from the external
  storage.

Note

The internal URL is always available, but the filesystem URL is only
available when the attachment is stored in an external storage.
Particular attention has been paid to limit as much as possible the
consumption of resources necessary to serve via Odoo the content stored
in an external filesystem. The implementation is based on an end-to-end
streaming of content between the external filesystem and the Odoo client
application by default. Nevertheless, if your content is available via a
URL on the external filesystem, you can configure the storage to use the
x-sendfile mechanism to serve the content if it's activated on your Odoo
instance. In this case, the content served by Odoo at the internal URL
will be proxied to the filesystem URL by nginx.

Last but not least, the addon adds a new method open on the attachment.
This method allows you to open the attachment as a file. For attachments
stored into the filestore or in an external filesystem, it allows you to
directly read from and write to the file and therefore minimize the
memory consumption since data are not kept into memory before being
written into the database.

Code Analysis

Views touched (1)
XML IDNameModelTypeStatus
fs_storage_form_view fs.storage.form (in fs_attachment) fs.storage form Inherits fs_storage.fs_storage_form_view
HTTP endpoints (0)

No HTTP endpoints found for this module.

Models touched (4)

New fields (2)
  • fs_storage_code Char
    args: 'Storage Code'
  • store_fname Char
    args: 'Stored Filename'
Public methods (0)

No public methods.

New fields (9)
  • autovacuum_gc Boolean
    default=True help='If checked, the autovacuum of the garbage collection will be automatically executed when the storage is used to store attachments. Sometime, the autovacuum is to avoid when files in the storage are referenced by other systems (like a website). In such case, records in the fs.file.gc table must be manually processed.' string='Autovacuum Garbage Collection'
  • base_url Char
    default=''
  • base_url_for_files Char
    compute='_compute_base_url_for_files' store=True
  • force_db_for_default_attachment_rules Text
    default=<expr> help='When storing attachments in an external storage, storage may be slow.If the storage is used to store odoo attachments by default, this could lead to a bad user experience since small images (128, 256) are used in Odoo in list / kanban views. We want them to be fast to read.This field allows to force the store of some attachments in the odoo database. The value is a dict Where the key is the beginning of the mimetype to configure and the value is the limit in size below which attachments are kept in DB. 0 means no limit.\nDefault configuration means:\n* images mimetypes (image/png, image/jpeg, ...) below 50KB are stored in database\n* application/javascript are stored in database whatever their size \n* text/css are stored in database whatever their size'
  • is_directory_path_in_url Boolean
    default=False help='Normally the directory_path is for internal usage. If this flag is enabled the path will be used to compute the public URL.'
  • optimizes_directory_path Boolean
    help="If checked, the directory path will be optimized to avoid too much files into the same directory. This options is used when the storage is used to store attachments. Depending on the storage, this option can be ignored. It's useful for storage based on real file. This way, files with similar properties will be stored in the same directory, avoiding overcrowding in the root directory and optimizing access times."
  • use_as_default_for_attachments Boolean
    default=False help='If checked, this storage will be used to store all the attachments '
  • use_filename_obfuscation Boolean
    help='If checked, the filename will be obfuscated. This option is useful to avoid to expose sensitive information trough the URL or in the remote storage. The obfuscation is done using a hash of the filename. The original filename is stored in the attachment metadata. The obfusation is to avoid if the storage is used to store files that are referenced by other systems (like a website) where the filename is important for SEO.'
  • use_x_sendfile_to_serve_internal_url Boolean
    help="If checked and odoo is behind a proxy that supports x-sendfile, the content served by the attachment's internal URL will be servedby the proxy using the fs_url if defined. If not, the file will be served by odoo that will stream the content read from the filesystem storage. This option is useful to avoid to serve files from odoo and therefore to avoid to load the odoo process. " string='Use X-Sendfile To Serve Internal Url'
Public methods (6)
  • create(self, vals_list)
    @api.model_create_multi
  • get_default_storage_code_for_attachments(self)
    @api.model
    Return the code of the storage to use to store the attachments. If the resource field is linked to a particular storage, return this one. Otherwise if the resource model is linked to a particular storage, return it. Finally return the code of the storage to use by default.
  • get_force_db_for_default_attachment_rules(self, code)
    @api.model@tools.ormcache('code')
    Return the rules to force the storage of some attachments in the DB :param code: the code of the storage :return: a dict where the key is the beginning of the mimetype to configure and the value is the limit in size below which attachments are kept in DB. 0 means no limit.
  • get_storage_code_for_attachments_fallback(self)
    @api.model@tools.ormcache()
  • recompute_urls(self) -> None
    Recompute the URL of all attachments since the base_url or the directory_path has changed. This method must be explicitly called by the user since we don't want to recompute the URL on each change of the base_url or directory_path. We could also have cases where such a recompute is not wanted. For example, when you restore a database from production to staging, you don't want to recompute the URL of the attachments created in production (since the directory_path use in production is readonly for the staging database) but you change the directory_path of the staging database to ensure that all the moditications in staging are done in a different directory and will not impact the production.
  • write(self, vals)

New fields (6)
  • fs_filename Char
    help='The name of the file in the filesystem storage.To preserve the mimetype and the meaning of the filenamethe filename is computed from the name and the extension' readonly=True args: 'File Name into the filesystem storage'
  • fs_storage_code Char
    related='fs_storage_id.code' store=True args: 'Filesystem Storage Code'
  • fs_storage_id Many2one → fs.storage
    compute='_compute_fs_storage_id' help='The storage where the file is stored.' ondelete='restrict' store=True args: 'fs.storage', 'Filesystem Storage'
  • fs_url Char
    compute='_compute_fs_url' help='The URL to access the file from the filesystem storage.' store=True args: 'Filesystem URL'
  • fs_url_path Char
    compute='_compute_fs_url_path' help='The path to access the file from the filesystem storage.' args: 'Filesystem URL Path'
  • internal_url Char
    compute='_compute_internal_url' help='The URL to access the file from the server.' args: 'Internal URL'
Public methods (5)
  • create(self, vals_list)
    @api.model_create_multi
    Storage may depend on resource field, but the method calling _storage (_get_datas_related_values) does not take all vals, just the mimetype. The only way to give res_field and res_model to _storage method is to pass them into the context, and perform 1 create call per record to create.
  • force_storage(self)
    @api.model
  • force_storage_to_db_for_special_fields(self, new_cr=False, storage: <expr>=None)
    @api.model
    Migrate special attachments from Object Storage back to database The access to a file stored on the objects storage is slower than a local disk or database access. For attachments like image_small that are accessed in batch for kanban views, this is too slow. We store this type of attachment in the database. This method can be used when migrating a filestore where all the files, including the special files (assets, image_small, ...) have been pushed to the Object Storage and we want to write them back in the database. It is not called anywhere, but can be called by RPC or scripts.
  • open(self, mode='rb', block_size=None, cache_options=None, compression=None, new_version=True, **kwargs) -> io.IOBase
    Return a file-like object from the filesystem storage where the attachment content is stored. In read mode, this method works for all attachments, even if the content is stored in the database or into the odoo filestore or a filesystem storage. The resultant instance must function correctly in a context ``with`` block. (parameters are ignored in the case of the database storage). Parameters ---------- path: str Target file mode: str like 'rb', 'w' See builtin ``open()`` block_size: int Some indication of buffering - this is a value in bytes cache_options : dict, optional Extra arguments to pass through to the cache. compression: string or None If given, open file using compression codec. Can either be a compression name (a key in ``fsspec.compression.compr``) or "infer" to guess the compression from the filename suffix. new_version: bool If True, and mode is 'w', create a new version of the file. If False, and mode is 'w', overwrite the current version of the file. This flag is True by default to avoid data loss and ensure transaction mechanism between Odoo and the filesystem storage. encoding, errors, newline: passed on to TextIOWrapper for text mode Returns ------- A file-like object TODO if open with 'w' in mode, we could use a buffered IO detecting that the content is modified and invalidating the attachment cache...
  • write(self, vals)

New fields (0)

No new fields.

Public methods (0)

No public methods.

Loading…

Loading…

Loading…