问题
- [ 精简指纹的作用,一直没理解]
- [为什么pilot2 上传文件的成功回调不用通知前端已上传数量]
基本概念
文件指纹:文件指纹是指文件的MD5值,是文件打包后输出的文件名的后缀。文件指纹在软件开发、版本控制和网络安全等领域都发挥着重要作用,确保文件的正确性和完整性。
功能概述
Pilot 2或者大疆机场把飞行器上的媒体文件下载到遥控器/机场的本地存储,然后通过网络上传到第三方服务器,媒体上传包含自动上传和手动上传功能
- 文件快传 : /media/api/v1/workspaces/{workspace_id}/fast-upload
传输文件时可能会存在云端已有文件,那么在Pilot2或机场上传文件时,会启动文件快传接口,如果已存在,直接返回上传成功@Override public HttpResultResponse mediaFastUpload(String workspaceId, @Valid MediaFastUploadRequest request, HttpServletRequest req, HttpServletResponse rsp) { // 根据文件指纹 查询数据库是否存在该文件 boolean isExist = mediaService.fastUpload(workspaceId, request.getFingerprint()); return isExist ? HttpResultResponse.success() : HttpResultResponse.error(request.getFingerprint() + "don't exist."); }
- 获取已存在文件的精简指纹:/media/api/v1/workspaces/{workspace_id}/files/tiny-fingerprints
通过精简指纹校验,确认文件是否已经上传
@Override
public HttpResultResponse<GetFileFingerprintResponse> getExistFileTinyFingerprint(String workspaceId, @Valid GetFileFingerprintRequest request, HttpServletRequest req, HttpServletResponse rsp) {
List<String> existingList = mediaService.getExistTinyFingerprints(workspaceId, request.getTinyFingerprints());
return HttpResultResponse.success(new GetFileFingerprintResponse().setTinyFingerprints(existingList));
}
- 获取上传临时凭证:
Pilot2: /storage/api/v1/workspaces/{workspace_id}/sts
每次媒体上传时,需要向服务端获取临时文件上传凭证,这样DJI Pilot 2在上传时会带上该凭证给对象存储服务进行校验。
@Override
public HttpResultResponse<StsCredentialsResponse> getTemporaryCredential(String workspaceId, HttpServletRequest req, HttpServletResponse rsp) {
StsCredentialsResponse stsCredentials = storageService.getSTSCredentials();
return HttpResultResponse.success(stsCredentials);
}
// 根据不同的provider 获取不同的临时凭证
@Override
public StsCredentialsResponse getSTSCredentials() {
return new StsCredentialsResponse()
.setEndpoint(OssConfiguration.endpoint)
.setBucket(OssConfiguration.bucket)
// 调用具体的对象存储类获取临时凭证信息
.setCredentials(ossService.getCredentials())
.setProvider(OssConfiguration.provider)
.setObjectKeyPrefix(OssConfiguration.objectDirPrefix)
.setRegion(OssConfiguration.region);
}
机场:通过 thing/product/{gateway_sn}/requests 主题,method:storage_config_get的mqtt消息,请求云端获取上传临时凭证。
method:storage_config_get的mqtt消息对应的实体类为:StorageConfigGet。将消息序列化为实体类后,再根据消息中的method属性,将requests请求消息路由到不同的管道中,method:storage_config_get的mqtt消息路由的管道为ChannelName.INBOUND_REQUESTS_STORAGE_CONFIG_GET
@ServiceActivator(inputChannel = ChannelName.INBOUND_REQUESTS_STORAGE_CONFIG_GET, outputChannel = ChannelName.OUTBOUND_REQUESTS)
public TopicRequestsResponse<MqttReply<StsCredentialsResponse>> storageConfigGet(TopicRequestsRequest<StorageConfigGet> request, MessageHeaders headers) {
throw new UnsupportedOperationException("storageConfigGet not implemented");
}
@Override
public TopicRequestsResponse<MqttReply<StsCredentialsResponse>> storageConfigGet(TopicRequestsRequest<StorageConfigGet> response, MessageHeaders headers) {
return new TopicRequestsResponse<MqttReply<StsCredentialsResponse>>().setData(MqttReply.success(getSTSCredentials()));
}
@Override
public StsCredentialsResponse getSTSCredentials() {
return new StsCredentialsResponse()
.setEndpoint(OssConfiguration.endpoint)
.setBucket(OssConfiguration.bucket)
.setCredentials(ossService.getCredentials())
.setProvider(OssConfiguration.provider)
.setObjectKeyPrefix(OssConfiguration.objectDirPrefix)
.setRegion(OssConfiguration.region);
}
-
媒体文件上传
Pilot2或者机场获取到临时凭证信息后,满足不同条件下,会自动将媒体文件上传到指定的对象存储中。
Pilot2需要满足以下条件之一:Pilot2设置了媒体文件自动上传,Pilot2中检测到媒体文件后即自动上传 Pilot2设置媒体文件手动上传,需要用户在Pilot2的媒体模块手动将媒体文件上传到对象存储中。
机场需要在执行完任务后,且无人机生成了媒体文件后,机场自动将媒体文件上传到云端。
5. 文件组上传完成后回调
Pilot2 :/media/api/v1/workspaces/{workspace_id}/upload-callback
@Override
public HttpResultResponse<String> mediaUploadCallback(String workspaceId, @Valid MediaUploadCallbackRequest request, HttpServletRequest req, HttpServletResponse rsp) {
// 将结果写入数据库中
mediaService.saveMediaFile(workspaceId, request);
return HttpResultResponse.success(request.getObjectKey());
}
机场:通过 thing/product/{gateway_sn}/events 主题,method:file_upload_callback的mqtt消息:
method:file_upload_callback的mqtt消息对应的实体类为:FileUploadCallback。将消息序列化为实体类后,再根据消息中的method属性,将requests请求消息路由到不同的管道中,method:file_upload_callback的mqtt消息路由的管道为ChannelName.INBOUND_EVENTS_FILE_UPLOAD_CALLBACK
@ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_FILE_UPLOAD_CALLBACK, outputChannel = ChannelName.OUTBOUND_EVENTS)
public TopicEventsResponse<MqttReply> fileUploadCallback(TopicEventsRequest<FileUploadCallback> request, MessageHeaders headers) {
throw new UnsupportedOperationException("fileUploadCallback not implemented");
}
@Override
public TopicEventsResponse<MqttReply> fileUploadCallback(TopicEventsRequest<FileUploadCallback> request, MessageHeaders headers) {
FileUploadCallback callback = request.getData();
if (MqttReply.CODE_SUCCESS != callback.getResult()) {
log.error("Media file upload failed!");
return null;
}
// 获取任务id
String jobId = callback.getFile().getExt().getFlightId();
Optional<DeviceDTO> deviceOpt = deviceRedisService.getDeviceOnline(request.getGateway());
MediaFileCountDTO mediaFileCount = mediaRedisService.getMediaCount(request.getGateway(), jobId);
// 重复数据
if (deviceOpt.isEmpty()
|| (Objects.nonNull(mediaFileCount) && request.getBid().equals(mediaFileCount.getBid())
&& request.getTid().equals(mediaFileCount.getTid()))) {
return new TopicEventsResponse<MqttReply>().setData(MqttReply.success());
}
DeviceDTO device = deviceOpt.get();
// 保存文件到数据库中
boolean isSave = parseMediaFile(callback, device);
if (!isSave) {
log.error("Failed to save the file to the database, please check the data manually.");
return null;
}
// 更新并通知已上传文件数量
notifyUploadedCount(mediaFileCount, request, jobId, device);
return new TopicEventsResponse<MqttReply>().setData(MqttReply.success());
}