Grails中如何获取已上传文件大小并在GSP页面展示
代码失效的根本原因
${this.game.thumbnail.size()}无法正常拿到文件大小,本质是当前的thumbnail属性根本不是可直接读取文件大小的java.io.File实例:
- 自定义上传逻辑下,
thumbnail一般存储的是文件在服务器的相对路径字符串,此时调用size()返回的是路径文本的字符长度,和实际文件大小完全无关 - 用了第三方附件插件的场景下,
thumbnail可能是关联值对象,但该对象没有暴露可访问的size()方法,或是存在懒加载未触发的问题 - 就算是把文件字节流存在数据库Blob字段中,直接在视图层调用方法也可能因为权限、连接问题读取失败
可落地的实现方案
方案1:Domain层预存文件大小字段(生产环境首选)
这个方案最稳定,没有运行时IO风险:
- 在
Game域类中新增一个持久化字段Long thumbnailSize,专门存储缩略图的字节大小 - 在文件上传的业务逻辑中,拿到上传的
MultipartFile对象时,直接把multipartFile.size()的返回值存入该字段,和文件存储、数据落库的流程绑定,保证数据一致性 - 在GSP页面直接读取该字段,配合Grails内置的
formatBytes标签自动转成人类可读格式,示例代码:
<div class='fieldcontain required'> <leve><b>缩略图文件大小:</b></leve> <leve> <g:if test="${this.game.thumbnailSize}"> ${formatBytes(bytes: this.game.thumbnailSize)} </g:if> </leve> </div>
方案2:Controller层实时计算文件大小传入视图
如果不想修改Domain结构,就把文件读取逻辑放在渲染页面的Controller方法里,不要在GSP中直接做IO操作:
- 示例Controller逻辑:
def show(Long id) { def game = Game.get(id) long thumbnailSize = 0 // 缩略图存在时,根据存储路径定位实际文件 if (game?.thumbnail) { File realThumbnail = new File(文件存储根路径, game.thumbnail) if (realThumbnail.exists()) { // length()方法返回文件的实际字节数 thumbnailSize = realThumbnail.length() } } // 把计算好的大小值一起传入页面Model [game: game, thumbnailSize: thumbnailSize] }
- 页面中直接读取Model返回的
thumbnailSize值做展示即可,避免GSP层抛文件不存在、权限不足的运行时异常
方案3:使用Attachmentable附件插件的正确读取方式
如果项目用了Grails生态常用的Attachmentable附件插件,之前读取失败是因为调用方式不对,插件自带的附件实例是内置size属性的,正确的读取逻辑为:
- 先通过域类的插件方法取出关联的附件对象,再读取它的size属性,示例GSP代码:
<g:set var="thumbAttachment" value="${game.getAttachment('thumbnail')}" /> <g:if test="${thumbAttachment}"> <leve>${formatBytes(bytes: thumbAttachment.size)}</leve> </g:if>
常见踩坑提示
- 不要在GSP视图层直接做文件IO操作:视图层没有独立的文件系统访问上下文,很容易出现路径错位、权限拦截的问题,所有涉及文件读取的逻辑都应放在Controller/Service层
- 不要把字符串的
size()结果当文件大小:如果thumbnail存的是路径文本,比如/upload/game1/thumb.jpg,调用.size()返回的是这段文本的字符长度,和实际文件体积没有任何关系 - 注意懒加载问题:如果域类关联了附件对象但未做抓取,直接在视图层调用方法可能触发Hibernate懒加载异常,提前在查询逻辑中把关联数据抓取完成可以大幅减少运行时问题
内容的提问来源于stack exchange,提问作者Sparsh Gahlot
相关产品推荐
相关产品推荐

