{"record":{"id":"6ed6a6adebfee32a","repo":"YunaiV/yudao-cloud","slug":"error-6ed6a6","errorCode":null,"errorMessage":"计算文件签名失败: ","messagePattern":"计算文件签名失败: ","errorType":"exception","errorClass":"RuntimeException","httpStatus":500,"severity":"error","filePath":"yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareServiceImpl.java","lineNumber":61,"sourceCode":"    private IotProductService productService;\n\n    @Override\n    public Long createOtaFirmware(IotOtaFirmwareCreateReqVO saveReqVO) {\n        // 1.1 校验固件产品 + 版本号不能重复\n        if (otaFirmwareMapper.selectByProductIdAndVersion(saveReqVO.getProductId(), saveReqVO.getVersion()) != null) {\n            throw exception(OTA_FIRMWARE_PRODUCT_VERSION_DUPLICATE);\n        }\n        // 1.2 校验产品存在\n        productService.validateProductExists(saveReqVO.getProductId());\n\n        // 2. 构建对象 + 存储\n        IotOtaFirmwareDO firmware = BeanUtils.toBean(saveReqVO, IotOtaFirmwareDO.class);\n        // 2.1 计算文件签名等属性\n        try {\n            calculateFileDigest(firmware);\n        } catch (Exception e) {\n            log.error(\"[createOtaFirmware][url({}) 计算文件签名失败]\", firmware.getFileUrl(), e);\n            throw new RuntimeException(\"计算文件签名失败: \" + e.getMessage());\n        }\n        otaFirmwareMapper.insert(firmware);\n        return firmware.getId();\n    }\n\n    @Override\n    public void updateOtaFirmware(IotOtaFirmwareUpdateReqVO updateReqVO) {\n        // 1. 校验存在\n        validateFirmwareExists(updateReqVO.getId());\n\n        // 2. 更新数据\n        IotOtaFirmwareDO updateObj = BeanUtils.toBean(updateReqVO, IotOtaFirmwareDO.class);\n        otaFirmwareMapper.updateById(updateObj);\n    }\n\n    @Override\n    public IotOtaFirmwareDO getOtaFirmware(Long id) {\n        return otaFirmwareMapper.selectById(id);","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/YunaiV/yudao-cloud/blob/477be9dd49ab7223a972a6abdff0684d6423dec3/yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareServiceImpl.java#L43-L79","documentation":"Thrown by IotOtaFirmwareServiceImpl.createOtaFirmware when the private helper calculateFileDigest(firmware) fails. That helper downloads the firmware file with HttpUtil.downloadBytes(fileUrl), sets fileSize, then computes an MD5 digest. Any exception from the download (bad/unreachable URL, 404 from the file service, I/O timeout) or the digest step is caught, logged, and rethrown as a generic RuntimeException whose message is '计算文件签名失败: ' plus the root cause's message.","triggerScenarios":"Calling createOtaFirmware (or the POST /iot/ota/firmware/create endpoint) with a fileUrl that: points to a host the server cannot reach, returns 404 because the file was not yet uploaded / was deleted, has a typo or wrong scheme, or when the file storage (e.g. MinIO/S3/local file service) is down. The exception message after the colon is whatever downloadBytes/digest threw (e.g. '404: Not Found', 'Connection refused', 'timeout').","commonSituations":"Client uploaded the file asynchronously and created the firmware record before the file was actually available; wrong file-server base URL in config; file service credentials/bucket misconfigured; network egress blocked from the app server; very large firmware files hitting the HTTP download timeout; URL stored without host (relative path) so downloadBytes fails.","solutions":["Decode the root cause: read the log line '[createOtaFirmware][url({}) 计算文件签名失败]' and the cause after '计算文件签名失败: ' — a 404 means the file is not at that URL, a connection error means the file server is unreachable.","Verify the URL by fetching it directly (curl -I) from the app server host; fix the upload or the file-service base URL in configuration if it 404s or cannot connect.","Ensure the file upload completes before createOtaFirmware is called (upload first, then create with the returned URL); re-run the create request.","If the file is large, raise the HTTP client timeout used by HttpUtil.downloadBytes (e.g. HttpUtil.downloadBytes(url, timeout) or global hutool HTTP timeouts).","Replace the raw RuntimeException with the project's error-code pattern (e.g. OTA_FIRMWARE_FILE_DIGEST_CALCULATE_FAILED) so clients get a structured GlobalExceptionHandler response instead of a 500."],"exampleFix":"// before\n} catch (Exception e) {\n    log.error(\"[createOtaFirmware][url({}) 计算文件签名失败]\", firmware.getFileUrl(), e);\n    throw new RuntimeException(\"计算文件签名失败: \" + e.getMessage());\n}\n\n// after (structured error code + preserved cause)\n} catch (Exception e) {\n    log.error(\"[createOtaFirmware][url({}) 计算文件签名失败]\", firmware.getFileUrl(), e);\n    throw exception(OTA_FIRMWARE_FILE_DIGEST_CALCULATE_FAILED, firmware.getFileUrl());\n}","handlingStrategy":"try-catch","validationCode":"// Validate the firmware URL is reachable before calling createOtaFirmware\nString fileUrl = saveReqVO.getFileUrl();\ntry {\n    HttpResponse resp = HttpRequest.head(fileUrl).timeout(10_000).execute();\n    if (!resp.isOk()) {\n        throw new IllegalArgumentException(\"Firmware file not available at \" + fileUrl + \": HTTP \" + resp.getStatus());\n    }\n} catch (HttpException e) {\n    throw new IllegalArgumentException(\"Cannot reach firmware file URL \" + fileUrl, e);\n}","typeGuard":null,"tryCatchPattern":"try {\n    firmwareService.createOtaFirmware(saveReqVO);\n} catch (RuntimeException e) {\n    if (e.getMessage() != null && e.getMessage().startsWith(\"计算文件签名失败\")) {\n        // file download/digest failed: verify the file URL and file service, then retry\n    } else {\n        throw e;\n    }\n}","preventionTips":["Upload the firmware file first and only call createOtaFirmware with the URL returned by the file service.","Add @NotBlank/@URL validation on fileUrl in the create VO so malformed URLs fail at the controller boundary.","Monitor file-service health; most digest failures are 404s or connection failures to the storage backend.","Prefer a typed error code over matching the message string; message-based catching is brittle across versions."],"tags":["iot","ota","firmware","file-download","md5","http","runtime-exception","java"],"backgroundTag":null,"analyzedSha":"477be9dd49ab7223a972a6abdff0684d6423dec3","analyzedAt":"2026-08-14T13:35:31.121Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}