You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Grails从URL下载文件异常:添加TFTP上传后下载失效问题

问题分析与解决方案

核心问题:对Grails redirect机制的误解

你之前用redirect实现的“下载”,本质是让客户端直接跳转到厂商URL去下载文件,服务器端本身并没有获取到固件文件的内容。所以当后续执行TFTP上传逻辑时,服务器本地根本不存在这个固件文件,自然会报错。

正确的实现思路

要完成“服务器端下载固件→上传到TFTP”的自动化流程,必须让服务器主动从厂商网站获取文件内容,而不是把下载任务甩给客户端。具体步骤:

  • 服务器端用HTTP客户端工具直接下载厂商固件到本地临时文件
  • 验证下载文件的完整性(比如检查文件大小、哈希值)
  • 用TFTP客户端将本地文件上传到TFTP服务器
  • 清理本地临时文件

Grails 示例代码实现

以下是一个简化的Service实现,用到Spring的RestTemplate做HTTP下载,Apache Commons Net的TFTPClient做TFTP上传:

import org.springframework.web.client.RestTemplate
import org.springframework.http.HttpMethod
import org.apache.commons.net.tftp.TFTPClient
import java.io.File
import java.io.FileOutputStream
import java.io.InputStream

class FirmwareAutoUpdateService {

    void processFirmware(String vendorFirmwareUrl, String tftpServer, int tftpPort, String tftpRemoteFileName) {
        File tempFirmwareFile = null
        try {
            // 1. 服务器端主动下载固件到临时文件
            tempFirmwareFile = File.createTempFile("temp_firmware", ".bin")
            RestTemplate restTemplate = new RestTemplate()
            
            restTemplate.execute(vendorFirmwareUrl, HttpMethod.GET, null, { response ->
                InputStream inputStream = response.body
                FileOutputStream outputStream = new FileOutputStream(tempFirmwareFile)
                
                byte[] buffer = new byte[4096]
                int bytesRead
                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, bytesRead)
                }
                
                outputStream.flush()
                outputStream.close()
                inputStream.close()
                return null
            })

            // 2. 验证文件完整性(示例:检查文件大小不为0,实际可加哈希校验)
            if (tempFirmwareFile.length() <= 0) {
                throw new RuntimeException("固件文件下载不完整,文件大小为0")
            }

            // 3. 上传到TFTP服务器
            TFTPClient tftpClient = new TFTPClient()
            tftpClient.connect(tftpServer, tftpPort)
            
            FileInputStream tftpInputStream = new FileInputStream(tempFirmwareFile)
            tftpClient.sendFile(tftpRemoteFileName, TFTPClient.BINARY_MODE, tftpInputStream)
            
            tftpInputStream.close()
            tftpClient.disconnect()

            println("固件上传TFTP服务器成功")
        } catch (Exception e) {
            println("处理失败: ${e.message}")
            throw e
        } finally {
            // 4. 清理临时文件
            if (tempFirmwareFile?.exists()) {
                tempFirmwareFile.delete()
            }
        }
    }
}

注意事项

  • 若厂商固件URL需要身份认证(比如API密钥、Cookie),需在RestTemplate中添加对应的请求头
  • 确保TFTP服务器允许写入指定路径,且服务器防火墙开放TFTP端口(默认69,UDP协议)
  • 可添加日志记录、重试机制来提升流程的稳定性
  • 临时文件存储路径可根据服务器配置调整,避免权限问题

内容的提问来源于stack exchange,提问作者Simon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 00:44:56