diff --git a/src/main/java/com/genersoft/iot/vmp/conf/ftpServer/ftplet.java b/src/main/java/com/genersoft/iot/vmp/conf/ftpServer/ftplet.java index 28cc2c1c3..ce035c705 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/ftpServer/ftplet.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/ftpServer/ftplet.java @@ -34,39 +34,38 @@ public class ftplet extends DefaultFtplet { @Override public FtpletResult onUploadEnd(FtpSession session, FtpRequest request) throws FtpException, IOException { - FtpUploadEvent event = new FtpUploadEvent(this); - event.setFileName(session.getFileSystemView().getFile(request.getArgument()).getAbsolutePath()); - applicationEventPublisher.publishEvent(event); - - logger.info("[文件已上传]: {}", session.getFileSystemView().getFile(request.getArgument()).getAbsolutePath()); - return super.onUploadEnd(session, request); + FtpFile file = session.getFileSystemView().getFile(request.getArgument()); + if (file == null) { + return super.onUploadEnd(session, request); + } + sendEvent(file.getAbsolutePath()); + return super.onUploadUniqueEnd(session, request); } @Override public FtpletResult onAppendEnd(FtpSession session, FtpRequest request) throws FtpException, IOException { - FtpUploadEvent event = new FtpUploadEvent(this); - String argument = request.getArgument(); - FileSystemView fileSystemView = session.getFileSystemView(); - FtpFile file = fileSystemView.getFile(request.getArgument()); - event.setFileName(session.getFileSystemView().getFile(request.getArgument()).getAbsolutePath()); - applicationEventPublisher.publishEvent(event); - - logger.info("[文件已上传]: {}", session.getFileSystemView().getFile(request.getArgument()).getAbsolutePath()); - return super.onUploadEnd(session, request); + FtpFile file = session.getFileSystemView().getFile(request.getArgument()); + if (file == null) { + return super.onUploadEnd(session, request); + } + sendEvent(file.getAbsolutePath()); + return super.onUploadUniqueEnd(session, request); } @Override public FtpletResult onUploadUniqueEnd(FtpSession session, FtpRequest request) throws FtpException, IOException { - FtpUploadEvent event = new FtpUploadEvent(this); - event.setFileName(session.getFileSystemView().getFile(request.getArgument()).getAbsolutePath()); - applicationEventPublisher.publishEvent(event); - - logger.info("[文件已上传]: {}", session.getFileSystemView().getFile(request.getArgument()).getAbsolutePath()); - return super.onUploadEnd(session, request); + FtpFile file = session.getFileSystemView().getFile(request.getArgument()); + if (file == null) { + return super.onUploadEnd(session, request); + } + sendEvent(file.getAbsolutePath()); + return super.onUploadUniqueEnd(session, request); } - @Override - public FtpletResult onDownloadStart(FtpSession session, FtpRequest request) throws FtpException, IOException { - return super.onDownloadStart(session, request); + private void sendEvent(String filePath){ + FtpUploadEvent event = new FtpUploadEvent(this); + logger.info("[文件已上传]: {}", filePath); + event.setFileName(filePath); + applicationEventPublisher.publishEvent(event); } } diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/controller/JT1078Controller.java b/src/main/java/com/genersoft/iot/vmp/jt1078/controller/JT1078Controller.java index f63751bf2..2f594fd38 100644 --- a/src/main/java/com/genersoft/iot/vmp/jt1078/controller/JT1078Controller.java +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/controller/JT1078Controller.java @@ -17,6 +17,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; +import org.apache.commons.compress.utils.IOUtils; import org.apache.commons.io.monitor.FileAlterationListenerAdaptor; import org.apache.commons.io.monitor.FileAlterationMonitor; import org.apache.commons.io.monitor.FileAlterationObserver; @@ -25,6 +26,7 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.http.MediaType; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.util.ObjectUtils; import org.springframework.web.bind.annotation.*; @@ -36,8 +38,11 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; +import java.net.URLEncoder; +import java.nio.file.Files; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -367,7 +372,7 @@ public class JT1078Controller { @Parameter(name = "type", description = "0.音视频 1.音频 2.视频 3.视频或音视频", required = true) @Parameter(name = "rate", description = "0.所有码流 1.主码流 2.子码流(如果此通道只传输音频,此字段置0)", required = true) @GetMapping("/playback/download") - public void recordDownload(HttpServletRequest request, + public DeferredResult recordDownload(HttpServletRequest request, HttpServletResponse response, @Parameter(required = true) String deviceId, @Parameter(required = false) String channelId, @@ -382,10 +387,38 @@ public class JT1078Controller { if (!ftpSetting.getEnable()) { throw new ControllerException(ErrorCode.ERROR100.getCode(), "未启用ftp服务,无法下载录像"); } + DeferredResult result = new DeferredResult<>(); ServletOutputStream outputStream = response.getOutputStream(); - service.recordDownload(deviceId, channelId, startTime, endTime, type, rate, outputStream); + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); + response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(deviceId + "_" + channelId + ".mp4", "UTF-8")); + response.setStatus(HttpServletResponse.SC_OK); + service.recordDownload(deviceId, channelId, startTime, endTime, type, rate, (code, msg, data) -> { + String filePath = "ftp" + data; + File file = new File(filePath); + if (!file.exists()) { + logger.warn("[下载录像] 收到通知时未找到录像文件: {}", filePath); + return; + } + try { + final InputStream in = Files.newInputStream(file.toPath()); + IOUtils.copy(in, outputStream); + outputStream.flush(); + in.close(); + } catch (IOException e) { + logger.warn("[下载录像] 读取文件异常: {}", filePath, e); + return; + } finally { + try { + outputStream.close(); + result.setResult(null); + } catch (IOException ignored) { + } + } + }); + return result; } + @Operation(summary = "1078-分页查询部标设备", security = @SecurityRequirement(name = JwtUtils.HEADER)) @Parameter(name = "page", description = "当前页", required = true) @Parameter(name = "count", description = "每页查询数量", required = true) diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/service/Ijt1078Service.java b/src/main/java/com/genersoft/iot/vmp/jt1078/service/Ijt1078Service.java index 272cfd2ef..e393a5a33 100644 --- a/src/main/java/com/genersoft/iot/vmp/jt1078/service/Ijt1078Service.java +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/service/Ijt1078Service.java @@ -117,5 +117,5 @@ public interface Ijt1078Service { void playbackControl(String deviceId, String channelId, Integer command, Integer playbackSpeed, String time); - void recordDownload(String deviceId, String channelId, String startTime, String endTime, Integer type, Integer rate, ServletOutputStream outputStream); + void recordDownload(String deviceId, String channelId, String startTime, String endTime, Integer type, Integer rate, GeneralCallback fileCallback); } diff --git a/src/main/java/com/genersoft/iot/vmp/jt1078/service/impl/jt1078ServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/jt1078/service/impl/jt1078ServiceImpl.java index b5b88c128..99a16c516 100644 --- a/src/main/java/com/genersoft/iot/vmp/jt1078/service/impl/jt1078ServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/jt1078/service/impl/jt1078ServiceImpl.java @@ -47,6 +47,7 @@ import org.springframework.stereotype.Service; import javax.servlet.ServletOutputStream; import java.io.*; import java.lang.reflect.Field; +import java.nio.file.Files; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -424,7 +425,7 @@ public class jt1078ServiceImpl implements Ijt1078Service { playbackControl(deviceId, channelId, 2, null, String.valueOf(0)); } - private Map fileUploadMap = new ConcurrentHashMap<>(); + private Map> fileUploadMap = new ConcurrentHashMap<>(); @EventListener public void onApplicationEvent(FtpUploadEvent event) { @@ -435,34 +436,21 @@ public class jt1078ServiceImpl implements Ijt1078Service { if (!event.getFileName().contains(key)) { return; } - ServletOutputStream servletOutputStream = fileUploadMap.get(event.getFileName()); - String filePath = "ftp" + event.getFileName(); - File file = new File(filePath); - if (!file.exists()) { - logger.warn("[下载录像] 收到通知时未找到录像文件: {}", filePath); - return; - } - try { - FileInputStream fileInputStream = new FileInputStream(file); - IOUtils.copy(fileInputStream, servletOutputStream); - fileInputStream.close(); - servletOutputStream.close(); - } catch (IOException e) { - logger.warn("[下载录像] 读取文件异常: {}", filePath, e); - return; - } finally { - try { - servletOutputStream.close(); - } catch (IOException ignored) { - } + GeneralCallback callback = fileUploadMap.get(key); + if (callback != null) { + callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), event.getFileName()); + fileUploadMap.remove(key); } }); } @Override - public void recordDownload(String deviceId, String channelId, String startTime, String endTime, Integer type, Integer rate, ServletOutputStream outputStream) { + public void recordDownload(String deviceId, String channelId, String startTime, String endTime, Integer type, Integer rate, GeneralCallback fileCallback) { String filePath = UUID.randomUUID().toString(); - fileUploadMap.put(filePath, outputStream); + fileUploadMap.put(filePath, fileCallback); + dynamicTask.startDelay(filePath, ()->{ + fileUploadMap.remove(filePath); + }, 2*60*60*1000); logger.info("[1078-录像] 下载,设备:{}, 通道: {}, 开始时间: {}, 结束时间: {},等待上传文件路径: {} ", deviceId, channelId, startTime, endTime, filePath); // 发送停止命令