新增根据 指定时间范围的 recordInfo 查询, 预下载视频文件到本地指定路径
This commit is contained in:
parent
67f48ad730
commit
92fb1555fd
@ -4,25 +4,22 @@ import cn.hutool.core.date.DatePattern;
|
|||||||
import cn.hutool.core.date.DateUtil;
|
import cn.hutool.core.date.DateUtil;
|
||||||
import cn.hutool.core.io.IoUtil;
|
import cn.hutool.core.io.IoUtil;
|
||||||
import cn.hutool.core.net.url.UrlBuilder;
|
import cn.hutool.core.net.url.UrlBuilder;
|
||||||
import cn.skcks.docking.gb28181.common.json.JsonException;
|
|
||||||
import cn.skcks.docking.gb28181.common.json.JsonResponse;
|
import cn.skcks.docking.gb28181.common.json.JsonResponse;
|
||||||
import cn.skcks.docking.gb28181.mocking.config.sip.DeviceProxyConfig;
|
import cn.skcks.docking.gb28181.mocking.config.sip.DeviceProxyConfig;
|
||||||
import cn.skcks.docking.gb28181.mocking.core.sip.executor.MockingExecutor;
|
import cn.skcks.docking.gb28181.mocking.core.sip.executor.MockingExecutor;
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
|
import org.apache.hc.client5.http.classic.methods.HttpGet;
|
||||||
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
|
import org.apache.hc.client5.http.config.ConnectionConfig;
|
||||||
import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
|
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
|
||||||
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
|
import org.apache.hc.client5.http.impl.classic.HttpClients;
|
||||||
import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
|
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
|
||||||
import org.apache.hc.core5.concurrent.FutureCallback;
|
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.scheduling.annotation.Async;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@ -30,10 +27,7 @@ import java.nio.channels.FileChannel;
|
|||||||
import java.nio.channels.FileLock;
|
import java.nio.channels.FileLock;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.concurrent.ConcurrentMap;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
@ -46,6 +40,16 @@ public class VideoCacheManager {
|
|||||||
|
|
||||||
private final ConcurrentMap<String, CompletableFuture<JsonResponse<String>>> tasks = new ConcurrentHashMap<>();
|
private final ConcurrentMap<String, CompletableFuture<JsonResponse<String>>> tasks = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private final PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager();
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
private void init(){
|
||||||
|
manager.setDefaultConnectionConfig(
|
||||||
|
ConnectionConfig.custom()
|
||||||
|
.setConnectTimeout(5, TimeUnit.MINUTES)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
public String dateFormat(Date date){
|
public String dateFormat(Date date){
|
||||||
return DateUtil.format(date, DatePattern.PURE_DATETIME_PATTERN);
|
return DateUtil.format(date, DatePattern.PURE_DATETIME_PATTERN);
|
||||||
}
|
}
|
||||||
@ -54,16 +58,13 @@ public class VideoCacheManager {
|
|||||||
return StringUtils.joinWith("-", deviceCode, dateFormat(startTime), dateFormat(endTime));
|
return StringUtils.joinWith("-", deviceCode, dateFormat(startTime), dateFormat(endTime));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Async(MockingExecutor.EXECUTOR_BEAN_NAME)
|
|
||||||
public void addTask(String deviceCode, Date startTime, Date endTime){
|
public void addTask(String deviceCode, Date startTime, Date endTime){
|
||||||
String name = fileName(deviceCode, startTime, endTime);
|
String name = fileName(deviceCode, startTime, endTime);
|
||||||
if(tasks.get(name) != null){
|
if(tasks.get(name) != null){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CompletableFuture<JsonResponse<String>> future = new CompletableFuture<>();
|
tasks.put(name, downloadVideo(deviceCode,startTime,endTime));
|
||||||
tasks.put(name, future);
|
|
||||||
downloadVideo(deviceCode,startTime,endTime, future);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompletableFuture<JsonResponse<String>> get(String deviceCode, Date startTime, Date endTime){
|
public CompletableFuture<JsonResponse<String>> get(String deviceCode, Date startTime, Date endTime){
|
||||||
@ -72,60 +73,46 @@ public class VideoCacheManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@Async(MockingExecutor.EXECUTOR_BEAN_NAME)
|
protected CompletableFuture<JsonResponse<String>> downloadVideo(String deviceCode, Date startTime, Date endTime) {
|
||||||
protected void downloadVideo(String deviceCode, Date startTime, Date endTime, CompletableFuture<JsonResponse<String>> future) {
|
return CompletableFuture.supplyAsync(()->{
|
||||||
final String url = UrlBuilder.of(deviceProxyConfig.getUrl())
|
final String url = UrlBuilder.of(deviceProxyConfig.getUrl())
|
||||||
.addPath("video")
|
.addPath("video")
|
||||||
.addQuery("device_id", deviceCode)
|
.addQuery("device_id", deviceCode)
|
||||||
.addQuery("begin_time", dateFormat(startTime))
|
.addQuery("begin_time", dateFormat(startTime))
|
||||||
.addQuery("end_time", dateFormat(endTime))
|
.addQuery("end_time", dateFormat(endTime))
|
||||||
.addQuery("useDownload", true).build();
|
.addQuery("useDownload", true).build();
|
||||||
File file = Paths.get(deviceProxyConfig.getPreDownloadForRecordInfo().getCachePath(),fileName(deviceCode, startTime, endTime) + ".mp4.tmp").toFile();
|
File file = Paths.get(deviceProxyConfig.getPreDownloadForRecordInfo().getCachePath(),fileName(deviceCode, startTime, endTime) + ".mp4.tmp").toFile();
|
||||||
log.info("文件存储路径 => {}", file.getAbsolutePath());
|
log.info("文件存储路径 => {}", file.getAbsolutePath());
|
||||||
log.info("文件 {}, 是否存在: {}", file.getAbsolutePath(), file.exists());
|
log.info("文件 {}, 是否存在: {}", file.getAbsolutePath(), file.exists());
|
||||||
|
|
||||||
if(file.exists()){
|
if(file.exists()){
|
||||||
file.delete();
|
file.delete();
|
||||||
log.info("删除已存但未完成下载的文件 => {}", file.getAbsolutePath());
|
log.info("删除已存但未完成下载的文件 => {}", file.getAbsolutePath());
|
||||||
}
|
|
||||||
|
|
||||||
try (FileOutputStream outputStream = new FileOutputStream(file)) {
|
|
||||||
FileChannel channel = outputStream.getChannel();
|
|
||||||
FileLock lock = channel.lock();
|
|
||||||
|
|
||||||
try (CloseableHttpAsyncClient asyncClient = HttpAsyncClients.custom().build()) {
|
|
||||||
asyncClient.start();
|
|
||||||
SimpleHttpRequest request = SimpleRequestBuilder.get(url).build();
|
|
||||||
asyncClient.execute(request, new FutureCallback<>() {
|
|
||||||
@SneakyThrows
|
|
||||||
@Override
|
|
||||||
public void completed(SimpleHttpResponse response) {
|
|
||||||
InputStream inputStream = new ByteArrayInputStream(response.getBodyBytes());
|
|
||||||
IoUtil.copy(inputStream, outputStream);
|
|
||||||
log.info("视频下载完成 => {}", file.getAbsolutePath());
|
|
||||||
log.info("文件 {}, 是否存在: {}", file.getAbsolutePath(), file.exists());
|
|
||||||
File realFile = Paths.get(deviceProxyConfig.getPreDownloadForRecordInfo().getCachePath(),fileName(deviceCode, startTime, endTime) + ".mp4").toFile();
|
|
||||||
file.renameTo(realFile);
|
|
||||||
lock.release();
|
|
||||||
future.complete(JsonResponse.success(file.getAbsolutePath()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SneakyThrows
|
|
||||||
@Override
|
|
||||||
public void failed(Exception ex) {
|
|
||||||
log.info("视频下载失败 => {}, {}", file.getAbsolutePath(), url);
|
|
||||||
lock.release();
|
|
||||||
future.completeExceptionally(ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SneakyThrows
|
|
||||||
@Override
|
|
||||||
public void cancelled() {
|
|
||||||
lock.release();
|
|
||||||
future.completeExceptionally(new JsonException("视频下载失败"));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
try (FileOutputStream outputStream = new FileOutputStream(file)) {
|
||||||
|
FileChannel channel = outputStream.getChannel();
|
||||||
|
FileLock lock = channel.lock();
|
||||||
|
|
||||||
|
try (CloseableHttpClient client = HttpClients.custom().setConnectionManager(manager).build()) {
|
||||||
|
HttpGet httpGet = new HttpGet(url);
|
||||||
|
client.execute(httpGet, response -> {
|
||||||
|
InputStream stream = response.getEntity().getContent();
|
||||||
|
IoUtil.copy(stream,outputStream);
|
||||||
|
return stream;
|
||||||
|
});
|
||||||
|
log.info("视频下载完成 => {}", file.getAbsolutePath());
|
||||||
|
log.info("文件 {}, 是否存在: {}", file.getAbsolutePath(), file.exists());
|
||||||
|
File realFile = Paths.get(deviceProxyConfig.getPreDownloadForRecordInfo().getCachePath(),fileName(deviceCode, startTime, endTime) + ".mp4").toFile();
|
||||||
|
file.renameTo(realFile);
|
||||||
|
lock.release();
|
||||||
|
return JsonResponse.success(realFile.getAbsolutePath());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("视频下载失败 => {}", e.getMessage());
|
||||||
|
file.delete();
|
||||||
|
return JsonResponse.error(e.getMessage());
|
||||||
|
}
|
||||||
|
},executor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user