YunaiV/yudao-cloud · error · RuntimeException
计算文件签名失败:
Error message
计算文件签名失败:
What it means
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.
Source
Thrown at yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareServiceImpl.java:61
private IotProductService productService;
@Override
public Long createOtaFirmware(IotOtaFirmwareCreateReqVO saveReqVO) {
// 1.1 校验固件产品 + 版本号不能重复
if (otaFirmwareMapper.selectByProductIdAndVersion(saveReqVO.getProductId(), saveReqVO.getVersion()) != null) {
throw exception(OTA_FIRMWARE_PRODUCT_VERSION_DUPLICATE);
}
// 1.2 校验产品存在
productService.validateProductExists(saveReqVO.getProductId());
// 2. 构建对象 + 存储
IotOtaFirmwareDO firmware = BeanUtils.toBean(saveReqVO, IotOtaFirmwareDO.class);
// 2.1 计算文件签名等属性
try {
calculateFileDigest(firmware);
} catch (Exception e) {
log.error("[createOtaFirmware][url({}) 计算文件签名失败]", firmware.getFileUrl(), e);
throw new RuntimeException("计算文件签名失败: " + e.getMessage());
}
otaFirmwareMapper.insert(firmware);
return firmware.getId();
}
@Override
public void updateOtaFirmware(IotOtaFirmwareUpdateReqVO updateReqVO) {
// 1. 校验存在
validateFirmwareExists(updateReqVO.getId());
// 2. 更新数据
IotOtaFirmwareDO updateObj = BeanUtils.toBean(updateReqVO, IotOtaFirmwareDO.class);
otaFirmwareMapper.updateById(updateObj);
}
@Override
public IotOtaFirmwareDO getOtaFirmware(Long id) {
return otaFirmwareMapper.selectById(id);View on GitHub (pinned to 477be9dd49)
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.
Example fix
// before
} catch (Exception e) {
log.error("[createOtaFirmware][url({}) 计算文件签名失败]", firmware.getFileUrl(), e);
throw new RuntimeException("计算文件签名失败: " + e.getMessage());
}
// after (structured error code + preserved cause)
} catch (Exception e) {
log.error("[createOtaFirmware][url({}) 计算文件签名失败]", firmware.getFileUrl(), e);
throw exception(OTA_FIRMWARE_FILE_DIGEST_CALCULATE_FAILED, firmware.getFileUrl());
} Defensive patterns
Strategy: try-catch
Validate before calling
// Validate the firmware URL is reachable before calling createOtaFirmware
String fileUrl = saveReqVO.getFileUrl();
try {
HttpResponse resp = HttpRequest.head(fileUrl).timeout(10_000).execute();
if (!resp.isOk()) {
throw new IllegalArgumentException("Firmware file not available at " + fileUrl + ": HTTP " + resp.getStatus());
}
} catch (HttpException e) {
throw new IllegalArgumentException("Cannot reach firmware file URL " + fileUrl, e);
} Try / catch
try {
firmwareService.createOtaFirmware(saveReqVO);
} catch (RuntimeException e) {
if (e.getMessage() != null && e.getMessage().startsWith("计算文件签名失败")) {
// file download/digest failed: verify the file URL and file service, then retry
} else {
throw e;
}
} Prevention
- 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.
When it happens
Trigger: 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').
Common situations: 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.
Related errors
AI-assisted analysis of YunaiV/yudao-cloud@477be9dd49 (2026-08-14).
Data as JSON: /api/errors/6ed6a6adebfee32a.
Report an issue: GitHub.