dodo升级13
Postgresql 常用操作
dodo数据库升级步骤
dodo13全模块升级
Ubuntu挂载硬盘
dodo13 测试问题记录
自定义功能
注册系统服务
13新功能
13源码修改
z
重置 JetBrains 试用时间
runbot环境
odoo相关网站
dodo随手记录
odoo接口安全
dodo单点登录
单点登录说明
接口说明
单点登录时序图
odoo安全
odoo安全性政策
odoo安全披露政策
GPT
2023062901_dodo临时生成文件并下载
本文档使用 MrDoc 发布
-
+
首页
2023062901_dodo临时生成文件并下载
# 起因 在 dodo 中经常会遇到一种需求:用户点击某个按钮后自动下载后台临时生成的一个文件。 在 odoo 的设计中,点击一个按钮后可以返回各种不同的 `action`,如:`ir.actions.act_window` 、`ir.actions.act_url`、`ir.actions.client`,但是这些 `action` 都不支持下载文件。 因此考虑添加一个 `ir.actions.act_down` 用于支持点击按钮后下载文件的需求。 # 当前解决方案 用户点击按钮后,后台返回 `ir.actions.act_down` 。 ```python def btn_export_bom(self): return { 'type': 'ir.actions.act_down', 'url': '/dtdream_product/dtdream_bom_export', 'datas': {'id':self.id,'uid': self._uid} } ``` 前端接收到 `ir.actions.act_down` 时,将 datas 作为参数,调用 url 接口获取文件并下载。 > [!example]- 后台接口: 生成附件并下载 > > ```python > @http.route('/dtdream_product/dtdream_bom_export', type='http', auth='public') > def dtdream_bom_export(self, data, token=None): > output = io.BytesIO() > workbook = xlsxwriter.Workbook(output, {'in_memory': True}) > ... # 填充表格内容 > workbook.close() > output.seek(0) > # 返回文件 > return request.make_response( > output.read(), > headers=[('Content-Disposition', self.content_disposition("Bom导出模板.xls")), > ('Content-Type', self.content_type)], > cookies={'fileToken': token} > ) > > ``` --- > [!example]- 前端添加对 `ir.actions.act_down` 的响应 > > ```js > odoo.define('dtdream.ir_actions_act_down', function (require) { > "use strict"; > > var ActionManager = require('web.ActionManager'); > var session = require('web.session'); > var Model = require('web.Model'); > > ActionManager.include({ > _handleAction: function (action, options) { > switch (action.type) { > case 'ir.actions.act_down': > return this.ir_actions_act_down(action); > default: > return this._super.apply(this, arguments); > } > }, > > ir_actions_act_down: function (action) { > var self = this; > $.blockUI(); > session.get_file({ > url: action.url, > data: {data: [JSON.stringify(action.datas)]}, > complete: $.unblockUI, > error: function (data) { > return new Model('raise.warning').call('raise_warning', [data.msg]); > } > }); > }, > }); > > }); > ``` ## 当前解决方案的问题 - 每个下载按钮都要添加一个 `http.route` 接口,接口太多不好管理 - 生成文件的代码放在 `http.route` 接口中,需要检查用户对接口的调用权限,没有对接口参数进行细致的检查很容易泄露数据。 --- # 改进的解决方案 主要改进点;将生成文件的代码放在按钮对应的函数中,以 uuid 为 key 将生成的文件及 header 信息写入 redis 中,前端通过固定的接口和 uuid 值获取文件。 改进后的好处: - 整个系统中只需要一个统一的 `http.route` 接口,减少了接口的数量,方便统计分析。 - `http.route` 接口中不需要进行权限判定和参数校验,降低因参数未严格校验导致的数据泄露风险。 - 添加新的下载按钮时,开发人员只需要实现 `btn_` 方法,不需要再添加 `http.route` 接口。 > [!example]- 统一的文件下载接口 > > ```python > @http.route('/dtdream_web/download/<string:_uuid>', type='http', auth="public") > def content_common(self, _uuid): > r = get_redis(db=4) > content = r.get(_uuid) > json_headers = json.loads(r.get(f'{_uuid}_headers') or '{}') > > if not content: > content = f"未找到要下载的资源,请尝试重新下载\n{_uuid}" > json_headers = { > 'Content-Disposition': f'attachment; filename=download_error.txt', > } > > new_headers = { > 'Content-Type': 'application/octet-strea', > 'Content-Length': len(content), > **json_headers, > } > headers = [(k, v) for k, v in new_headers.items()] > response = request.make_response(content, headers) > return response > ``` --- > [!example]- 按钮对应的文件生成方法 > > ```python > def btn_down_zip(self): > file = io.BytesIO() > > ... # 填充文件内容 > > return ir_actions_act_down(filename, file.getvalue()) > ``` --- > [!example]- 封装的 ir_actions_act_down 方法,将数据存入 redis 并返回动作 > > ```python > FILE_TYPE_DICT = { > 'zip': 'application/zip', > 'xls': 'application/vnd.ms-excel', > 'xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', > 'ods': 'application/vnd.oasis.opendocument.spreadsheet', > 'csv': 'text/csv', > 'gif': 'image/gif', > 'jpe': 'image/jpeg', > 'jpg': 'image/jpg', > 'png': 'image/png', > 'svg': 'image/svg+xml', > } > > def ir_actions_act_down(filename, file_content, ex=360): > r = get_redis(db=4) > > content_type = FILE_TYPE_DICT.get(filename.split('.')[-1], 'application/octet-stream') > > escaped = urllib.parse.quote(filename) > headers = { > 'Content-Disposition': f'attachment; filename={escaped}', > 'Content-Type': content_type, > } > _uuid = uuid.uuid4().hex > r.set(_uuid, file_content, ex=ex) > r.set(f'{_uuid}_headers', json.dumps(headers), ex=ex) > > return { > 'type': 'ir.actions.act_down', > 'url': f'/dtdream_web/download/{_uuid}', > 'datas': [] > } > ```
幻翼
2023年6月29日 15:54
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码