From 9958bc1501d0daee954d9bba383475e55e24ebab Mon Sep 17 00:00:00 2001
From: liusuyi <1951119284@qq.com>
Date: Thu, 14 May 2026 08:18:07 +0800
Subject: [PATCH] 优化
---
ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/impl/MediaServerServiceImpl.java | 83 +++++
ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/hp/service/HpSDK.java | 78 +++--
ard-modules/ard-modules-file/pom.xml | 4
ard-modules/ard-modules-file/src/main/java/com/ard/file/service/LocalSysFileServiceImpl.java | 10
ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/hik/service/AbstractHikVisionSDK.java | 221 +++++++++++----
ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/utils/ZLMRESTfulUtils.java | 2
ard-api/ard-api-system/src/main/java/com/ard/system/api/RemoteFileService.java | 6
ard-modules/ard-modules-file/src/main/java/com/ard/file/service/MinioSysFileServiceImpl.java | 90 ++++-
ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/dh/service/DaHuaSDK.java | 63 +++-
ard-modules/ard-modules-file/src/main/java/com/ard/file/controller/SysFileController.java | 38 ++
/dev/null | 125 --------
ard-modules/ard-modules-file/src/main/java/com/ard/file/service/ISysFileService.java | 3
ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/aspect/SdkOperateAspect.java | 12
ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/zlxd/service/ZlxdSDK.java | 68 +++-
14 files changed, 515 insertions(+), 288 deletions(-)
diff --git a/ard-api/ard-api-system/src/main/java/com/ard/system/api/RemoteFileService.java b/ard-api/ard-api-system/src/main/java/com/ard/system/api/RemoteFileService.java
index 5ed67d6..2e421c4 100644
--- a/ard-api/ard-api-system/src/main/java/com/ard/system/api/RemoteFileService.java
+++ b/ard-api/ard-api-system/src/main/java/com/ard/system/api/RemoteFileService.java
@@ -34,8 +34,8 @@
* @return 访问地址
* @throws Exception
*/
- @PostMapping(value = "upload/{bucketName}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
- public R<SysFile> upload(@RequestPart(value = "file") MultipartFile file,@PathVariable("bucketName") String bucketName);
+ @PostMapping(value = "/uploadWithBucket", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
+ public R<SysFile> upload(@RequestPart(value = "file") MultipartFile file,@RequestParam("bucketName") String bucketName);
/**
* 文件上传接口
@@ -46,7 +46,7 @@
* @return 访问地址
* @throws Exception
*/
- @PostMapping(value = "uploadWithPath", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
+ @PostMapping(value = "/uploadWithPath", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public R<SysFile> upload(@RequestPart("file") MultipartFile file, @RequestParam("bucketName") String bucketName, @RequestParam("path") String path);
/**
diff --git a/ard-modules/ard-modules-file/pom.xml b/ard-modules/ard-modules-file/pom.xml
index 959055b..37e3eb8 100644
--- a/ard-modules/ard-modules-file/pom.xml
+++ b/ard-modules/ard-modules-file/pom.xml
@@ -65,6 +65,10 @@
<groupId>com.ard</groupId>
<artifactId>ard-common-swagger</artifactId>
</dependency>
+ <dependency>
+ <groupId>com.ard</groupId>
+ <artifactId>ard-common-security</artifactId>
+ </dependency>
</dependencies>
<build>
diff --git a/ard-modules/ard-modules-file/src/main/java/com/ard/file/controller/SysFileController.java b/ard-modules/ard-modules-file/src/main/java/com/ard/file/controller/SysFileController.java
index 74c5d51..37e2b16 100644
--- a/ard-modules/ard-modules-file/src/main/java/com/ard/file/controller/SysFileController.java
+++ b/ard-modules/ard-modules-file/src/main/java/com/ard/file/controller/SysFileController.java
@@ -5,9 +5,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import com.ard.common.core.domain.R;
import com.ard.common.core.utils.StringUtils;
@@ -51,7 +49,39 @@
return R.fail(e.getMessage());
}
}
-
+ /**
+ * 文件上传请求(带目录参数)
+ * @param file 文件
+ * @param bucketName 桶名称
+ */
+ @PostMapping("uploadWithBucket")
+ public R<SysFile> uploadWithBucket(@RequestPart("file")MultipartFile file, @RequestParam("bucketName") String bucketName) {
+ try {
+ // 上传并返回访问地址
+ String url = sysFileService.uploadFile(file, bucketName);
+ SysFile sysFile = new SysFile();
+ sysFile.setName(FileUtils.getName(url));
+ sysFile.setUrl(url);
+ return R.ok(sysFile);
+ } catch (Exception e) {
+ log.error("上传文件失败", e);
+ return R.fail(e.getMessage());
+ }
+ }
+ @PostMapping("uploadWithPath")
+ public R<SysFile> uploadWithPath(@RequestPart("file") MultipartFile file, @RequestParam("bucketName") String bucketName, @RequestParam("path") String path) {
+ try {
+ // 上传并返回访问地址
+ String url = sysFileService.uploadFile(file, bucketName, path);
+ SysFile sysFile = new SysFile();
+ sysFile.setName(FileUtils.getName(url));
+ sysFile.setUrl(url);
+ return R.ok(sysFile);
+ } catch (Exception e) {
+ log.error("上传文件失败", e);
+ return R.fail(e.getMessage());
+ }
+ }
/**
* 文件删除请求
*/
diff --git a/ard-modules/ard-modules-file/src/main/java/com/ard/file/service/ISysFileService.java b/ard-modules/ard-modules-file/src/main/java/com/ard/file/service/ISysFileService.java
index fe9f68b..ba07686 100644
--- a/ard-modules/ard-modules-file/src/main/java/com/ard/file/service/ISysFileService.java
+++ b/ard-modules/ard-modules-file/src/main/java/com/ard/file/service/ISysFileService.java
@@ -17,7 +17,8 @@
* @throws Exception
*/
public String uploadFile(MultipartFile file) throws Exception;
-
+ public String uploadFile(MultipartFile file, String bucketName)throws Exception;
+ public String uploadFile(MultipartFile file, String bucketName, String path)throws Exception;
/**
* 文件删除接口
*
diff --git a/ard-modules/ard-modules-file/src/main/java/com/ard/file/service/LocalSysFileServiceImpl.java b/ard-modules/ard-modules-file/src/main/java/com/ard/file/service/LocalSysFileServiceImpl.java
index a6e9e42..27efa82 100644
--- a/ard-modules/ard-modules-file/src/main/java/com/ard/file/service/LocalSysFileServiceImpl.java
+++ b/ard-modules/ard-modules-file/src/main/java/com/ard/file/service/LocalSysFileServiceImpl.java
@@ -49,6 +49,16 @@
return url;
}
+ @Override
+ public String uploadFile(MultipartFile file, String bucketName) throws Exception {
+ return "";
+ }
+
+ @Override
+ public String uploadFile(MultipartFile file, String bucketName, String path) throws Exception {
+ return "";
+ }
+
/**
* 本地文件删除接口
*
diff --git a/ard-modules/ard-modules-file/src/main/java/com/ard/file/service/MinioSysFileServiceImpl.java b/ard-modules/ard-modules-file/src/main/java/com/ard/file/service/MinioSysFileServiceImpl.java
index 9d291d1..c151313 100644
--- a/ard-modules/ard-modules-file/src/main/java/com/ard/file/service/MinioSysFileServiceImpl.java
+++ b/ard-modules/ard-modules-file/src/main/java/com/ard/file/service/MinioSysFileServiceImpl.java
@@ -1,6 +1,9 @@
package com.ard.file.service;
import java.io.InputStream;
+
+import io.minio.GetPresignedObjectUrlArgs;
+import io.minio.http.Method;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
@@ -20,8 +23,7 @@
*/
@Primary
@Service
-public class MinioSysFileServiceImpl implements ISysFileService
-{
+public class MinioSysFileServiceImpl implements ISysFileService {
@Autowired
private MinioConfig minioConfig;
@@ -36,11 +38,9 @@
* @throws Exception
*/
@Override
- public String uploadFile(MultipartFile file) throws Exception
- {
+ public String uploadFile(MultipartFile file) throws Exception {
InputStream inputStream = null;
- try
- {
+ try {
String fileName = FileUploadUtils.extractFilename(file);
inputStream = file.getInputStream();
PutObjectArgs args = PutObjectArgs.builder()
@@ -51,33 +51,81 @@
.build();
client.putObject(args);
return minioConfig.getUrl() + "/" + minioConfig.getBucketName() + "/" + fileName;
- }
- catch (Exception e)
- {
+ } catch (Exception e) {
throw new RuntimeException("Minio Failed to upload file", e);
- }
- finally
- {
+ } finally {
IoUtils.closeQuietly(inputStream);
}
}
/**
+ * Minio文件上传接口
+ *
+ * @param file 上传的文件
+ * @param bucketName 桶名称
+ * @return 访问地址
+ * @author 刘苏义
+ * @description 指定桶名称上传文件
+ * @date 2024/8/5 11:37
+ */
+ @Override
+ public String uploadFile(MultipartFile file, String bucketName) throws Exception {
+ String fileName = FileUploadUtils.extractFilename(file);
+ InputStream inputStream = file.getInputStream();
+ PutObjectArgs args = PutObjectArgs.builder()
+ .bucket(bucketName)
+ .object(fileName)
+ .stream(inputStream, file.getSize(), -1)
+ .contentType(file.getContentType())
+ .build();
+ client.putObject(args);
+ IoUtils.closeQuietly(inputStream);
+ return minioConfig.getUrl() + "/" + bucketName + "/" + fileName;
+ }
+
+ @Override
+ public String uploadFile(MultipartFile file, String bucketName, String path) throws Exception {
+ //判断文件是否为空
+ if (null == file || 0 == file.getSize()) {
+ return "";
+ }
+ //文件名
+ String fileName = path + "/" + FileUploadUtils.extractFilename(file);
+ InputStream inputStream = file.getInputStream();
+ /*上传对象*/
+ PutObjectArgs putObjectArgs = PutObjectArgs
+ .builder()
+ .bucket(bucketName)
+ .object(fileName)
+ .stream(inputStream, file.getSize(), -1)
+ .contentType(file.getContentType())
+ .build();
+ client.putObject(putObjectArgs);
+ inputStream.close();
+ /*获取url*/
+ GetPresignedObjectUrlArgs getPresignedObjectUrlArgs = GetPresignedObjectUrlArgs
+ .builder()
+ .bucket(bucketName)
+ .object(fileName)
+ .method(Method.GET)
+ .build();
+ String presignedObjectUrl = client.getPresignedObjectUrl(getPresignedObjectUrlArgs);
+ String ObjectUrl = presignedObjectUrl.substring(0, presignedObjectUrl.indexOf("?"));
+ return ObjectUrl;
+ }
+
+ /**
* Minio文件删除接口
- *
+ *
* @param fileUrl 文件访问URL
* @throws Exception
*/
@Override
- public void deleteFile(String fileUrl) throws Exception
- {
- try
- {
- String minioFile = StringUtils.substringAfter(fileUrl, minioConfig.getBucketName()+ "/");
+ public void deleteFile(String fileUrl) throws Exception {
+ try {
+ String minioFile = StringUtils.substringAfter(fileUrl, minioConfig.getBucketName() + "/");
client.removeObject(RemoveObjectArgs.builder().bucket(minioConfig.getBucketName()).object(minioFile).build());
- }
- catch (Exception e)
- {
+ } catch (Exception e) {
throw new RuntimeException("Minio Failed to delete file", e);
}
}
diff --git a/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/aspect/SdkOperateAspect.java b/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/aspect/SdkOperateAspect.java
index c5f16ea..ef7322c 100644
--- a/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/aspect/SdkOperateAspect.java
+++ b/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/aspect/SdkOperateAspect.java
@@ -69,6 +69,18 @@
else if (Boolean.class.isAssignableFrom(returnType) || boolean.class == returnType) {
log.error(message);
return false;
+ }else if (String.class.isAssignableFrom(returnType)) {
+ // 【新增】处理 String 返回类型
+ log.error(message);
+ return "";
+ } else if (byte[].class.isAssignableFrom(returnType)) {
+ // 【新增】处理 byte[] 返回类型
+ log.error(message);
+ return new byte[0];
+ } else if (void.class == returnType || Void.class.isAssignableFrom(returnType)) {
+ // 处理 void 返回类型
+ log.error(message);
+ return null;
}
else {
// 其他未知类型,为了安全还是返回 null,但最好记录错误
diff --git a/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/dh/service/DaHuaSDK.java b/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/dh/service/DaHuaSDK.java
index 9b4c449..77b9795 100644
--- a/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/dh/service/DaHuaSDK.java
+++ b/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/dh/service/DaHuaSDK.java
@@ -5,6 +5,7 @@
import com.ard.common.core.exception.ServiceException;
import com.ard.common.core.exception.device.CameraSDKException;
import com.ard.common.core.utils.StringUtils;
+import com.ard.common.core.utils.file.FileMultipartFile;
import com.ard.common.core.utils.file.FileUtils;
import com.ard.common.core.utils.file.MimeTypeUtils;
import com.ard.common.redis.service.RedisService;
@@ -29,7 +30,6 @@
import com.ard.work.sdk.model.PtzScope;
import com.ard.work.sdk.service.CameraSDK;
import com.ard.work.utils.FFmpegUtils;
-import com.ard.work.utils.file.MultipartFileUtil;
import com.ard.work.utils.gis.GisUtil;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
@@ -44,6 +44,7 @@
import java.io.ByteArrayInputStream;
import java.io.File;
+import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
@@ -675,10 +676,12 @@
log.warn("转码耗时:{}", watchStop.getTotalTimeMillis());
//存入minio
String bucketName = "record";
- String objectName = cameraId + "_" + chanNo;
+ String fileName = cameraId + "_" + chanNo;
String type = cmd.getType();
- MultipartFile multipartFile = MultipartFileUtil.filePathToMultipartFile(newpath, objectName,
- MimeTypeUtils.VIDEO_MP4);
+// MultipartFile multipartFile = MultipartFileUtil.filePathToMultipartFile(newpath, objectName,
+// MimeTypeUtils.VIDEO_MP4);
+ File tempFile = new File(newpath);
+ MultipartFile multipartFile = new FileMultipartFile(tempFile, fileName, MimeTypeUtils.VIDEO_MP4);
R<SysFile> sysFileR = remoteFileService.upload(multipartFile, bucketName, type);
if (sysFileR.getCode() == R.SUCCESS) {
url = sysFileR.getData().getUrl();
@@ -1182,17 +1185,49 @@
public void invoke(LLong lLoginID, Pointer pBuf, int RevLen, int EncodeType, int CmdSerial, Pointer dwUser) {
if (pBuf != null && RevLen > 0) {
byte[] buf = pBuf.getByteArray(0, RevLen);
- //存储到minio
- InputStream inputStream = new ByteArrayInputStream(buf);
- MultipartFile multipartFile = MultipartFileUtil.inputStreamToMultipartFile(inputStream, objectName,
- MimeTypeUtils.IMAGE_JPEG);
- R<SysFile> sysFileR = remoteFileService.upload(multipartFile, bucketName, type);
- if (sysFileR.getCode() == R.SUCCESS) {
- String url = sysFileR.getData().getUrl();
- log.info("上传文件成功:{}", url);
- // 在回调中完成 CompletableFuture
- urlFuture.complete(url);
+
+ // 定义文件信息
+ String fileName = objectName;
+ String tempDir = System.getProperty("java.io.tmpdir") + "/snap_temp/";
+ File tempDirFile = new File(tempDir);
+ if (!tempDirFile.exists()) {
+ tempDirFile.mkdirs();
}
+ File tempFile = new File(tempDir + fileName);
+
+ try {
+ // 写入临时文件
+ try (FileOutputStream fos = new FileOutputStream(tempFile)) {
+ fos.write(buf);
+ fos.flush();
+ }
+
+ // 使用 FileMultipartFile 转换
+ MultipartFile multipartFile = new FileMultipartFile(tempFile, fileName, MimeTypeUtils.IMAGE_JPEG);
+
+ // 上传到 MinIO
+ R<SysFile> sysFileR = remoteFileService.upload(multipartFile, bucketName, type);
+
+ if (sysFileR.getCode() == R.SUCCESS) {
+ String url = sysFileR.getData().getUrl();
+ log.info("上传文件成功:{}", url);
+ urlFuture.complete(url);
+ } else {
+ log.error("上传文件失败: {}", sysFileR.getMsg());
+ urlFuture.completeExceptionally(new RuntimeException("上传失败: " + sysFileR.getMsg()));
+ }
+
+ } catch (Exception e) {
+ log.error("处理截图失败", e);
+ urlFuture.completeExceptionally(e);
+ } finally {
+ // 清理临时文件
+ if (tempFile.exists()) {
+ tempFile.delete();
+ }
+ }
+ } else {
+ urlFuture.completeExceptionally(new RuntimeException("截图数据为空"));
}
}
}
diff --git a/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/hik/service/AbstractHikVisionSDK.java b/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/hik/service/AbstractHikVisionSDK.java
index 9cca6f5..e4e7991 100644
--- a/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/hik/service/AbstractHikVisionSDK.java
+++ b/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/hik/service/AbstractHikVisionSDK.java
@@ -5,6 +5,7 @@
import com.ard.common.core.exception.ServiceException;
import com.ard.common.core.exception.device.CameraSDKException;
import com.ard.common.core.utils.StringUtils;
+import com.ard.common.core.utils.file.FileMultipartFile;
import com.ard.common.core.utils.file.FileUtils;
import com.ard.common.core.utils.file.MimeTypeUtils;
import com.ard.common.redis.service.RedisService;
@@ -30,7 +31,6 @@
import com.ard.work.sdk.model.PtzScope;
import com.ard.work.sdk.service.CameraSDK;
import com.ard.work.utils.FFmpegUtils;
-import com.ard.work.utils.file.MultipartFileUtil;
import com.ard.work.utils.gis.GisUtil;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
@@ -1561,6 +1561,7 @@
String type = cmd.getType() == null ? "other" : cmd.getType();
String bucketName = cmd.getBucketName() == null ? "pic" : cmd.getBucketName();
int userId = cmd.getLoginId().intValue();
+
//图片信息
NET_DVR_JPEGPARA jpeg = new NET_DVR_JPEGPARA();
//设置图片分辨率
@@ -1570,8 +1571,9 @@
IntByReference a = new IntByReference();
//设置图片大小
ByteBuffer jpegBuffer = ByteBuffer.allocate(1024 * 1024);
- // 抓图到内存,单帧数据捕获并保存成JPEG存放在指定的内存空间中
+ // 抓图到内存
boolean bool = hCNetSDK.NET_DVR_CaptureJPEGPicture_NEW(userId, chanNo, jpeg, jpegBuffer, 1024 * 1024, a);
+
if (!bool) {
int errorCode = hCNetSDK.NET_DVR_GetLastError();
String errorDesc = SdkErrorCodeEnum.getDescByCode(errorCode);
@@ -1579,19 +1581,46 @@
log.warn(errorMsg);
return "";
}
+
log.debug("hikSdk(抓图)-结果状态值(0表示成功):{}", hCNetSDK.NET_DVR_GetLastError());
- byte[] array = jpegBuffer.array();
- //存储到minio
- String objectName = cameraId + "_" + chanNo + ".jpeg";
- InputStream inputStream = new ByteArrayInputStream(array);
- String url = "";
- MultipartFile multipartFile = MultipartFileUtil.inputStreamToMultipartFile(inputStream, objectName,
- MimeTypeUtils.IMAGE_JPEG);
- R<SysFile> sysFileR = remoteFileService.upload(multipartFile, bucketName, type);
- if (sysFileR.getCode() == R.SUCCESS) {
- url = sysFileR.getData().getUrl();
- log.debug("上传文件成功:{}", url);
+
+ // 获取实际图片数据
+ int actualLength = a.getValue();
+ byte[] imageBytes;
+ if (actualLength > 0 && actualLength < 1024 * 1024) {
+ imageBytes = new byte[actualLength];
+ System.arraycopy(jpegBuffer.array(), 0, imageBytes, 0, actualLength);
+ } else {
+ imageBytes = jpegBuffer.array();
}
+
+ // 存储到minio
+ String objectName = cameraId + "_" + chanNo + ".jpeg";
+ String url = "";
+ File tempFile = null;
+
+ try {
+ // 创建临时文件
+ tempFile = File.createTempFile("snap_", ".jpeg");
+ java.nio.file.Files.write(tempFile.toPath(), imageBytes);
+
+ // 使用 FileMultipartFile 转换
+ MultipartFile multipartFile = new FileMultipartFile(tempFile, objectName, MimeTypeUtils.IMAGE_JPEG);
+
+ R<SysFile> sysFileR = remoteFileService.upload(multipartFile, bucketName, type);
+ if (sysFileR.getCode() == R.SUCCESS) {
+ url = sysFileR.getData().getUrl();
+ log.debug("上传文件成功:{}", url);
+ }
+ } catch (Exception e) {
+ log.error("上传失败", e);
+ } finally {
+ // 删除临时文件
+ if (tempFile != null && tempFile.exists()) {
+ tempFile.delete();
+ }
+ }
+
return url;
}
@@ -1602,6 +1631,7 @@
Integer chanNo = cmd.getChanNo();
String type = cmd.getType() == null ? "other" : cmd.getType();
Integer userId = cmd.getLoginId().intValue();
+
//图片信息
NET_DVR_JPEGPARA jpeg = new NET_DVR_JPEGPARA();
//设置图片分辨率
@@ -1613,6 +1643,7 @@
ByteBuffer jpegBuffer = ByteBuffer.allocate(1024 * 1024);
// 抓图到内存,单帧数据捕获并保存成JPEG存放在指定的内存空间中
boolean bool = hCNetSDK.NET_DVR_CaptureJPEGPicture_NEW(userId, chanNo, jpeg, jpegBuffer, 1024 * 1024, a);
+
if (!bool) {
int errorCode = hCNetSDK.NET_DVR_GetLastError();
String errorDesc = SdkErrorCodeEnum.getDescByCode(errorCode);
@@ -1620,22 +1651,50 @@
log.warn(errorMsg);
return "";
}
- byte[] array = jpegBuffer.array();
- InputStream inputStream = new ByteArrayInputStream(array);
- String url = "";
- MultipartFile multipartFile = MultipartFileUtil.inputStreamToMultipartFile(inputStream, objectName,
- MimeTypeUtils.IMAGE_JPEG);
- R<SysFile> sysFileR = remoteFileService.upload(multipartFile, bucketName, type);
- if (sysFileR.getCode() == R.SUCCESS) {
- url = sysFileR.getData().getUrl();
- log.debug("上传文件成功:{}", url);
+
+ // 获取实际图片数据(只复制有效长度)
+ int actualLength = a.getValue();
+ byte[] imageBytes;
+ if (actualLength > 0 && actualLength < 1024 * 1024) {
+ imageBytes = new byte[actualLength];
+ System.arraycopy(jpegBuffer.array(), 0, imageBytes, 0, actualLength);
+ } else {
+ imageBytes = jpegBuffer.array();
}
+
+ String url = "";
+ File tempFile = null;
+
+ try {
+ // 创建临时文件
+ tempFile = File.createTempFile("snap_", ".jpeg");
+ java.nio.file.Files.write(tempFile.toPath(), imageBytes);
+
+ // 使用 FileMultipartFile 转换
+ MultipartFile multipartFile = new FileMultipartFile(tempFile, objectName, MimeTypeUtils.IMAGE_JPEG);
+
+ R<SysFile> sysFileR = remoteFileService.upload(multipartFile, bucketName, type);
+ if (sysFileR.getCode() == R.SUCCESS) {
+ url = sysFileR.getData().getUrl();
+ log.debug("上传文件成功:{}", url);
+ }
+ } catch (Exception e) {
+ log.error("上传失败", e);
+ } finally {
+ if (tempFile != null && tempFile.exists()) {
+ tempFile.delete();
+ }
+ }
+
return url;
}
//短时录像
@Override
public String record(CameraCmd cmd) {
+ String url = "";
+ File tempFile = null;
+
try {
String cameraId = cmd.getCameraId();
Integer chanNo = cmd.getChanNo();
@@ -1644,29 +1703,32 @@
String path = FileUtils.createFile(tempDir + "/record/" + name + ".mp4");
boolean enable = cmd.isEnable();
Integer userId = cmd.getLoginId().intValue();
+
//强制I帧结构体对象
- NET_DVR_I_FRAME netDvrIFrame = new NET_DVR_I_FRAME(); //新建结构体对象
+ NET_DVR_I_FRAME netDvrIFrame = new NET_DVR_I_FRAME();
netDvrIFrame.read();
- netDvrIFrame.dwChannel = chanNo;//因为上文代码中设置了通道号,按照上文中的设置
+ netDvrIFrame.dwChannel = chanNo;
netDvrIFrame.byStreamType = 0;
netDvrIFrame.dwSize = netDvrIFrame.size();
netDvrIFrame.write();
+
if (!hCNetSDK.NET_DVR_RemoteControl(userId, 3402, netDvrIFrame.getPointer(), netDvrIFrame.dwSize)) {
log.error("强制I帧 错误码为:{}", hCNetSDK.NET_DVR_GetLastError());
}
+
//预览参数
NET_DVR_PREVIEWINFO previewInfo = new NET_DVR_PREVIEWINFO();
previewInfo.read();
previewInfo.lChannel = chanNo;
- previewInfo.dwStreamType = 0;//码流类型:0-主码流,1-子码流,2-三码流,3-虚拟码流,以此类推
- previewInfo.dwLinkMode = 0;//连接方式:0-TCP方式,1-UDP方式,2-多播方式,3-RTP方式,4-RTP/RTSP,5-RTP/HTTP,6-HRUDP(可靠传输),7
- // -RTSP/HTTPS,8-NPQ
- previewInfo.hPlayWnd = null;//播放窗口的句柄,为NULL表示不解码显示。
- previewInfo.bBlocked = 0;//0- 非阻塞取流,1-阻塞取流
- previewInfo.byNPQMode = 0;//NPQ模式:0-直连模式,1-过流媒体模式
+ previewInfo.dwStreamType = 0;
+ previewInfo.dwLinkMode = 0;
+ previewInfo.hPlayWnd = null;
+ previewInfo.bBlocked = 0;
+ previewInfo.byNPQMode = 0;
previewInfo.write();
- String url = "";
+
if (enable) {
+ // 开始录像
if (!GlobalVariable.previewMap.containsKey(name)) {
int lRealHandle = hCNetSDK.NET_DVR_RealPlay_V40(userId, previewInfo, null, null);
if (lRealHandle == -1) {
@@ -1676,6 +1738,7 @@
log.debug("取流成功");
GlobalVariable.previewMap.put(name, lRealHandle);
}
+
if (!hCNetSDK.NET_DVR_SaveRealData_V30((int) GlobalVariable.previewMap.get(name), 2, path)) {
log.error("保存视频文件到临时文件夹失败 错误码为:{}", hCNetSDK.NET_DVR_GetLastError());
return "";
@@ -1683,24 +1746,40 @@
log.debug("录像开始");
} else {
+ // 停止录像
if (GlobalVariable.previewMap.containsKey(name)) {
Integer lRealHandle = (Integer) GlobalVariable.previewMap.get(name);
hCNetSDK.NET_DVR_StopRealPlay(lRealHandle);
GlobalVariable.previewMap.remove(name);
}
log.debug("录像停止");
- //存入minio
+
+ // 检查录像文件是否存在
+ File videoFile = new File(path);
+ if (!videoFile.exists() || videoFile.length() == 0) {
+ log.error("录像文件不存在或为空: {}", path);
+ return "";
+ }
+
+ // 存入minio - 使用 FileMultipartFile
String bucketName = "record";
- String objectName = cameraId + "_" + chanNo;
- MultipartFile multipartFile = MultipartFileUtil.filePathToMultipartFile(path, objectName,
- MimeTypeUtils.VIDEO_MP4);
+ String objectName = cameraId + "_" + chanNo + ".mp4";
+
+ // 创建临时文件对象(实际上 path 已经是文件路径)
+ tempFile = new File(path);
+
+ // 使用 FileMultipartFile 转换
+ MultipartFile multipartFile = new FileMultipartFile(tempFile, objectName, MimeTypeUtils.VIDEO_MP4);
+
R<SysFile> sysFileR = remoteFileService.upload(multipartFile, bucketName);
if (sysFileR.getCode() == R.SUCCESS) {
url = sysFileR.getData().getUrl();
log.debug("上传文件成功:{}", url);
}
}
+
return url;
+
} catch (Exception ex) {
log.error("录像异常:{}", ex.getMessage());
return "";
@@ -1763,69 +1842,81 @@
return false;
}
}
-
@Override
public String recordStop(CameraCmd cmd) {
String url = "";
+ File videoFile = null;
+ File transcodeFile = null;
+
try {
String cameraId = cmd.getCameraId();
Integer chanNo = cmd.getChanNo();
String name = cameraId + "_" + chanNo;
// 本地临时录像地址
String path = FileUtils.createFile(tempDir + "/record/" + name + ".mp4");
- Integer userId = cmd.getLoginId().intValue();
- //region 强制I帧
- NET_DVR_I_FRAME netDvrIFrame = new NET_DVR_I_FRAME(); //新建结构体对象
- netDvrIFrame.read();
- netDvrIFrame.dwChannel = chanNo;//因为上文代码中设置了通道号,按照上文中的设置
- netDvrIFrame.byStreamType = 0;
- netDvrIFrame.dwSize = netDvrIFrame.size();
- netDvrIFrame.write();
- if (!hCNetSDK.NET_DVR_RemoteControl(userId, 3402, netDvrIFrame.getPointer(), netDvrIFrame.dwSize)) {
- log.error("强制I帧 错误码为:{}", hCNetSDK.NET_DVR_GetLastError());
- }
- //endregion
- //region 预览参数
- NET_DVR_PREVIEWINFO previewInfo = new NET_DVR_PREVIEWINFO();
- previewInfo.read();
- previewInfo.lChannel = chanNo;
- previewInfo.dwStreamType = 0;//码流类型:0-主码流,1-子码流,2-三码流,3-虚拟码流,以此类推
- previewInfo.dwLinkMode = 0;//连接方式:0-TCP方式,1-UDP方式,2-多播方式,3-RTP方式,4-RTP/RTSP,5-RTP/HTTP,6-HRUDP(可靠传输),7
- // -RTSP/HTTPS,8-NPQ
- previewInfo.hPlayWnd = null;//播放窗口的句柄,为NULL表示不解码显示。
- previewInfo.bBlocked = 0;//0- 非阻塞取流,1-阻塞取流
- previewInfo.byNPQMode = 0;//NPQ模式:0-直连模式,1-过流媒体模式
- previewInfo.write();
- //endregion
+
+ // 停止录像
if (GlobalVariable.previewMap.containsKey(name)) {
Integer lRealHandle = (Integer) GlobalVariable.previewMap.get(name);
hCNetSDK.NET_DVR_StopRealPlay(lRealHandle);
GlobalVariable.previewMap.remove(name);
}
log.debug("录像停止");
- //ffmpeg转码
+
+ // 检查原录像文件是否存在
+ videoFile = new File(path);
+ if (!videoFile.exists() || videoFile.length() == 0) {
+ log.error("录像文件不存在或为空: {}", path);
+ return "";
+ }
+
+ // ffmpeg转码
StopWatch watchStop = new StopWatch();
watchStop.start();
String newpath = FileUtils.createFile(tempDir + "/record/" + name + "_c.mp4");
+ transcodeFile = new File(newpath);
FFmpegUtils.transcodeToMP4(path, newpath);
watchStop.stop();
log.warn("转码耗时:{}", watchStop.getTotalTimeMillis());
- //存入minio
+
+ // 检查转码后的文件是否存在
+ if (!transcodeFile.exists() || transcodeFile.length() == 0) {
+ log.error("转码文件不存在或为空: {}", newpath);
+ return "";
+ }
+
+ // 存入minio - 使用 FileMultipartFile
String bucketName = "record";
- String objectName = cameraId + "_" + chanNo;
+ String objectName = cameraId + "_" + chanNo + ".mp4";
String type = cmd.getType();
- MultipartFile multipartFile = MultipartFileUtil.filePathToMultipartFile(newpath, objectName,
- MimeTypeUtils.VIDEO_MP4);
+
+ MultipartFile multipartFile = new FileMultipartFile(transcodeFile, objectName, MimeTypeUtils.VIDEO_MP4);
R<SysFile> sysFileR = remoteFileService.upload(multipartFile, bucketName, type);
+
if (sysFileR.getCode() == R.SUCCESS) {
url = sysFileR.getData().getUrl();
log.info("上传文件成功:{}", url);
}
return url;
+
} catch (Exception ex) {
log.error("录像异常:{}", ex.getMessage());
return "";
+ } finally {
+ // 清理临时文件
+ try {
+ if (videoFile != null && videoFile.exists()) {
+ videoFile.delete();
+ log.debug("删除原录像文件: {}", videoFile.getPath());
+ }
+ if (transcodeFile != null && transcodeFile.exists()) {
+ transcodeFile.delete();
+ log.debug("删除转码文件: {}", transcodeFile.getPath());
+ }
+ } catch (Exception e) {
+ log.warn("删除临时文件失败", e);
+ }
}
}
diff --git a/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/hp/service/HpSDK.java b/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/hp/service/HpSDK.java
index 57021a8..89916e9 100644
--- a/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/hp/service/HpSDK.java
+++ b/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/hp/service/HpSDK.java
@@ -5,6 +5,7 @@
import com.ard.common.core.exception.ServiceException;
import com.ard.common.core.exception.device.CameraSDKException;
import com.ard.common.core.utils.StringUtils;
+import com.ard.common.core.utils.file.FileMultipartFile;
import com.ard.common.core.utils.file.FileUtils;
import com.ard.common.core.utils.file.MimeTypeUtils;
import com.ard.common.redis.service.RedisService;
@@ -25,7 +26,6 @@
import com.ard.work.sdk.model.PtzScope;
import com.ard.work.utils.ArdTool;
import com.ard.work.utils.FFmpegUtils;
-import com.ard.work.utils.file.MultipartFileUtil;
import com.ard.work.utils.gis.GisUtil;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
@@ -37,6 +37,7 @@
import org.springframework.util.StopWatch;
import org.springframework.web.multipart.MultipartFile;
+import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@@ -1398,69 +1399,84 @@
private String hikRecordStop(CameraCmd cmd) {
String url = "";
+ File videoFile = null;
+ File transcodeFile = null;
+
try {
String cameraId = cmd.getCameraId();
Integer chanNo = cmd.getChanNo();
String name = cameraId + "_" + chanNo;
+
// 本地临时录像地址
String path = FileUtils.createFile(tempDir + "/record/" + name + ".mp4");
+
Long userId = redisService.getCacheMapValue(CacheConstants.CAMERA_ONLINE, cameraId);
if (userId == null) {
throw new ServiceException("设备离线");
}
- //region 强制I帧
- NET_DVR_I_FRAME netDvrIFrame = new NET_DVR_I_FRAME(); //新建结构体对象
- netDvrIFrame.read();
- netDvrIFrame.dwChannel = chanNo;//因为上文代码中设置了通道号,按照上文中的设置
- netDvrIFrame.byStreamType = 0;
- netDvrIFrame.dwSize = netDvrIFrame.size();
- netDvrIFrame.write();
- if (!hCNetSDK.NET_DVR_RemoteControl(userId.intValue(), 3402, netDvrIFrame.getPointer(),
- netDvrIFrame.dwSize)) {
- log.error("强制I帧 错误码为:{}", hCNetSDK.NET_DVR_GetLastError());
- }
- //endregion
- //region 预览参数
- NET_DVR_PREVIEWINFO previewInfo = new NET_DVR_PREVIEWINFO();
- previewInfo.read();
- previewInfo.lChannel = chanNo;
- previewInfo.dwStreamType = 0;//码流类型:0-主码流,1-子码流,2-三码流,3-虚拟码流,以此类推
- previewInfo.dwLinkMode = 0;//连接方式:0-TCP方式,1-UDP方式,2-多播方式,3-RTP方式,4-RTP/RTSP,5-RTP/HTTP,6-HRUDP(可靠传输),7
- // -RTSP/HTTPS,8-NPQ
- previewInfo.hPlayWnd = null;//播放窗口的句柄,为NULL表示不解码显示。
- previewInfo.bBlocked = 0;//0- 非阻塞取流,1-阻塞取流
- previewInfo.byNPQMode = 0;//NPQ模式:0-直连模式,1-过流媒体模式
- previewInfo.write();
- //endregion
+
+ // 停止录像
if (GlobalVariable.previewMap.containsKey(name)) {
Integer lRealHandle = (Integer) GlobalVariable.previewMap.get(name);
hCNetSDK.NET_DVR_StopRealPlay(lRealHandle);
GlobalVariable.previewMap.remove(name);
}
log.debug("录像停止");
- //ffmpeg转码
+
+ // 检查原录像文件是否存在
+ videoFile = new File(path);
+ if (!videoFile.exists() || videoFile.length() == 0) {
+ log.error("录像文件不存在或为空: {}", path);
+ return "";
+ }
+
+ // ffmpeg转码
StopWatch watchStop = new StopWatch();
watchStop.start();
String newpath = FileUtils.createFile(tempDir + "/record/" + name + "_c.mp4");
+ transcodeFile = new File(newpath);
FFmpegUtils.transcodeToMP4(path, newpath);
watchStop.stop();
log.warn("转码耗时:{}", watchStop.getTotalTimeMillis());
- //存入minio
+
+ // 检查转码后的文件是否存在
+ if (!transcodeFile.exists() || transcodeFile.length() == 0) {
+ log.error("转码文件不存在或为空: {}", newpath);
+ return "";
+ }
+
+ // 存入minio - 使用 FileMultipartFile
String bucketName = "record";
- String objectName = cameraId + "_" + chanNo;
- String type = cmd.getType();
- MultipartFile multipartFile = MultipartFileUtil.filePathToMultipartFile(newpath, objectName,
- MimeTypeUtils.VIDEO_MP4);
+ String objectName = cameraId + "_" + chanNo + ".mp4";
+ String type = cmd.getType() == null ? "record" : cmd.getType();
+
+ MultipartFile multipartFile = new FileMultipartFile(transcodeFile, objectName, MimeTypeUtils.VIDEO_MP4);
R<SysFile> sysFileR = remoteFileService.upload(multipartFile, bucketName, type);
+
if (sysFileR.getCode() == R.SUCCESS) {
url = sysFileR.getData().getUrl();
log.info("上传文件成功:{}", url);
}
return url;
+
} catch (Exception ex) {
log.error("录像异常:{}", ex.getMessage());
return "";
+ } finally {
+ // 清理临时文件
+ try {
+ if (videoFile != null && videoFile.exists()) {
+ videoFile.delete();
+ log.debug("删除原录像文件: {}", videoFile.getPath());
+ }
+ if (transcodeFile != null && transcodeFile.exists()) {
+ transcodeFile.delete();
+ log.debug("删除转码文件: {}", transcodeFile.getPath());
+ }
+ } catch (Exception e) {
+ log.warn("删除临时文件失败", e);
+ }
}
}
diff --git a/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/zlxd/service/ZlxdSDK.java b/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/zlxd/service/ZlxdSDK.java
index f32b656..5e2a7c2 100644
--- a/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/zlxd/service/ZlxdSDK.java
+++ b/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/zlxd/service/ZlxdSDK.java
@@ -6,6 +6,7 @@
import com.ard.common.core.exception.ServiceException;
import com.ard.common.core.exception.device.CameraSDKException;
import com.ard.common.core.utils.StringUtils;
+import com.ard.common.core.utils.file.FileMultipartFile;
import com.ard.common.core.utils.file.MimeTypeUtils;
import com.ard.common.redis.service.RedisService;
import com.ard.system.api.RemoteFileService;
@@ -27,7 +28,6 @@
import com.ard.work.sdk.zlxd.netty.tcp.NettyTcpClient;
import com.ard.work.sdk.zlxd.utils.PTZ3DLocationConverter;
import com.ard.work.sdk.zlxd.utils.XmlUtils;
-import com.ard.work.utils.file.MultipartFileUtil;
import com.ard.work.utils.gis.GisUtil;
import com.galaxy.sdk.RecordSdk;
import com.galaxy.sdk.SDKConfig;
@@ -774,17 +774,50 @@
log.warn("抓图失败,错误码:{},原因:{}", errorCode, errorDesc);
return "";
}
+
String base64Str = getRes.getParameters().getPicBuf();
- //存储到minio
+ if (base64Str == null || base64Str.isEmpty()) {
+ log.warn("Base64图片数据为空");
+ return "";
+ }
+
+ // 处理 Base64 前缀(如果有 data:image/jpeg;base64, 这样的前缀)
+ String pureBase64 = base64Str;
+ if (base64Str.contains(",")) {
+ pureBase64 = base64Str.split(",")[1];
+ }
+
+ // 存储到minio
String objectName = cameraId + "_" + chanNo + ".jpeg";
String url = "";
- MultipartFile multipartFile = MultipartFileUtil.base64ToMultipartFile(base64Str, objectName,
- MimeTypeUtils.IMAGE_JPEG);
- R<SysFile> sysFileR = remoteFileService.upload(multipartFile, bucketName, type);
- if (sysFileR.getCode() == R.SUCCESS) {
- url = sysFileR.getData().getUrl();
- log.debug("上传文件成功:{}", url);
+ File tempFile = null;
+
+ try {
+ // Base64 解码
+ byte[] imageBytes = Base64.getDecoder().decode(pureBase64);
+
+ // 创建临时文件
+ tempFile = File.createTempFile("snap_", ".jpeg");
+ java.nio.file.Files.write(tempFile.toPath(), imageBytes);
+
+ // 使用 FileMultipartFile 转换
+ MultipartFile multipartFile = new FileMultipartFile(tempFile, objectName, MimeTypeUtils.IMAGE_JPEG);
+
+ R<SysFile> sysFileR = remoteFileService.upload(multipartFile, bucketName, type);
+ if (sysFileR.getCode() == R.SUCCESS) {
+ url = sysFileR.getData().getUrl();
+ log.debug("上传文件成功:{}", url);
+ }
+
+ } catch (Exception e) {
+ log.error("上传失败", e);
+ } finally {
+ // 删除临时文件
+ if (tempFile != null && tempFile.exists()) {
+ tempFile.delete();
+ }
}
+
return url;
}
@@ -1075,18 +1108,20 @@
localFilePath = filePaths.get(0);
log.info("录像停止成功,本地文件: {}", localFilePath);
- // 3. 上传到 MinIO
- // 校验文件是否存在
- java.io.File file = new java.io.File(localFilePath);
+ // 3. 校验文件是否存在
+ File file = new File(localFilePath);
if (!file.exists()) {
log.error("录像文件不存在,无法上传: {}", localFilePath);
return "";
}
- MultipartFile multipartFile = MultipartFileUtil.filePathToMultipartFile(
- localFilePath, name, MediaType.APPLICATION_OCTET_STREAM_VALUE); // 建议使用具体的 MimeType
+ // 4. 使用 FileMultipartFile 转换(不用 MultipartFileUtil)
+ String objectName = cameraId + "_" + chanNo + ".mp4";
+ String type = cmd.getType() == null ? "record" : cmd.getType();
- R<SysFile> sysFileR = remoteFileService.upload(multipartFile, "record", cmd.getType());
+ MultipartFile multipartFile = new FileMultipartFile(file, objectName, MimeTypeUtils.VIDEO_MP4);
+
+ R<SysFile> sysFileR = remoteFileService.upload(multipartFile, "record", type);
if (sysFileR.getCode() == R.SUCCESS && sysFileR.getData() != null) {
String url = sysFileR.getData().getUrl();
@@ -1099,12 +1134,9 @@
} catch (Exception ex) {
log.error("停止录像过程发生异常: {}", name, ex);
- // 即使异常,SessionId 已经在第一步移除了,防止死锁
- // 但这里可能需要记录日志以便人工排查残留文件
return "";
} finally {
- // 4. 【重要】清理本地临时文件
- // 无论成功还是失败,只要生成了本地文件,上传完后都应该删除,防止磁盘爆满
+ // 5. 【重要】清理本地临时文件
if (localFilePath != null) {
try {
java.nio.file.Files.deleteIfExists(java.nio.file.Paths.get(localFilePath));
diff --git a/ard-modules/ard-modules-work/src/main/java/com/ard/work/utils/file/MultipartFileUtil.java b/ard-modules/ard-modules-work/src/main/java/com/ard/work/utils/file/MultipartFileUtil.java
deleted file mode 100644
index 90381c7..0000000
--- a/ard-modules/ard-modules-work/src/main/java/com/ard/work/utils/file/MultipartFileUtil.java
+++ /dev/null
@@ -1,125 +0,0 @@
-package com.ard.work.utils.file;
-
-import java.io.*;
-import java.util.Base64;
-
-import org.springframework.web.multipart.MultipartFile;
-
-public class MultipartFileUtil {
-
- /**
- * 通过InputStream获取MultipartFile
- */
- public static MultipartFile inputStreamToMultipartFile(InputStream inputStream, String fileName, String contentType) {
- try {
- byte[] content = inputStream.readAllBytes(); // Java 9+ 可用
- return new ByteArrayMultipartFile(content, "file", fileName, contentType);
- } catch (IOException e) {
- throw new RuntimeException("输入流转换失败: " + e.getMessage(), e);
- } finally {
- try {
- if (inputStream != null) {
- inputStream.close();
- }
- } catch (IOException e) {
- // 忽略关闭异常
- }
- }
- }
-
- /**
- * 通过文件路径获取MultipartFile
- */
- public static MultipartFile filePathToMultipartFile(String filePath, String fileName, String contentType) {
- File file = new File(filePath);
-
- if (!fileName.contains(".") && filePath.contains(".")) {
- String extension = filePath.substring(filePath.lastIndexOf('.'));
- fileName = fileName + extension;
- }
-
- try {
- byte[] content = java.nio.file.Files.readAllBytes(file.toPath());
- return new ByteArrayMultipartFile(content, "file", fileName, contentType);
- } catch (IOException e) {
- throw new RuntimeException("文件转换失败: " + e.getMessage(), e);
- }
- }
-
- /**
- * Base64字符串转MultipartFile
- */
- public static MultipartFile base64ToMultipartFile(String base64Str, String fileName, String contentType) {
- if (base64Str == null || base64Str.isEmpty()) {
- throw new IllegalArgumentException("Base64字符串不能为空");
- }
-
- String pureBase64Str = base64Str.contains(",") ? base64Str.split(",")[1] : base64Str;
-
- try {
- byte[] fileBytes = Base64.getDecoder().decode(pureBase64Str);
- return new ByteArrayMultipartFile(fileBytes, "file", fileName, contentType);
- } catch (IllegalArgumentException e) {
- throw new RuntimeException("Base64字符串格式非法,解码失败", e);
- }
- }
-
- /**
- * 自定义 MultipartFile 实现(不依赖 MockMultipartFile)
- */
- private static class ByteArrayMultipartFile implements MultipartFile {
- private final byte[] content;
- private final String name;
- private final String originalFilename;
- private final String contentType;
-
- public ByteArrayMultipartFile(byte[] content, String name, String originalFilename, String contentType) {
- this.content = content;
- this.name = name;
- this.originalFilename = originalFilename;
- this.contentType = contentType;
- }
-
- @Override
- public String getName() {
- return name;
- }
-
- @Override
- public String getOriginalFilename() {
- return originalFilename;
- }
-
- @Override
- public String getContentType() {
- return contentType;
- }
-
- @Override
- public boolean isEmpty() {
- return content == null || content.length == 0;
- }
-
- @Override
- public long getSize() {
- return content.length;
- }
-
- @Override
- public byte[] getBytes() throws IOException {
- return content;
- }
-
- @Override
- public InputStream getInputStream() throws IOException {
- return new ByteArrayInputStream(content);
- }
-
- @Override
- public void transferTo(File dest) throws IOException, IllegalStateException {
- try (FileOutputStream fos = new FileOutputStream(dest)) {
- fos.write(content);
- }
- }
- }
-}
\ No newline at end of file
diff --git a/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/impl/MediaServerServiceImpl.java b/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/impl/MediaServerServiceImpl.java
index e6c636f..e95947c 100644
--- a/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/impl/MediaServerServiceImpl.java
+++ b/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/impl/MediaServerServiceImpl.java
@@ -8,10 +8,13 @@
import com.ard.common.core.domain.RtpServerParam;
import com.ard.common.core.enums.LiveStreamType;
import com.ard.common.core.utils.DateUtils;
+import com.ard.common.core.utils.file.FileMultipartFile;
import com.ard.gb28181.api.RemoteGb28181Service;
import com.ard.gb28181.api.domain.Device;
import com.ard.qs.api.RemoteQsDeviceService;
import com.ard.qs.api.domain.QsDevice;
+import com.ard.system.api.RemoteFileService;
+import com.ard.system.api.domain.SysFile;
import com.ard.work.api.RemoteCameraService;
import com.ard.work.api.RemoteChannelService;
import com.ard.work.api.domian.ArdChannel;
@@ -50,7 +53,12 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.DigestUtils;
+import org.springframework.web.multipart.MultipartFile;
+import java.io.File;
+import java.io.FileInputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
import java.util.*;
/**
@@ -64,7 +72,8 @@
@Slf4j
@Service
public class MediaServerServiceImpl implements IMediaServerService {
-
+ @Resource
+ private RemoteFileService remoteFileService;
@Resource
private MediaServerMapper mediaServerMapper;
@@ -787,6 +796,28 @@
* @param app app
* @param stream 流id
*/
+ //@Override
+// public String snapOnPlay1(ZlmMediaServer mediaServer, String app, String stream) {
+// String fileName = app + "-" + stream + ".jpg";
+// // 请求截图
+// log.info("[请求截图]: " + fileName);
+//
+// IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
+// if (mediaNodeServerService == null) {
+// log.info("[getSnap] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType());
+// throw new RuntimeException("[getSnap] 失败, mediaServer的类型: " + mediaServer.getType() + ",未找到对应的实现类");
+// }
+// String filePath = fileDomain + filePrefix + "/snap/" + fileName;
+// mediaNodeServerService.getSnap(mediaServer, app, stream, 30, 300, this.filePath + "/snap", fileName);
+// return filePath;
+// }
+ /**
+ * 点播成功时调用截图
+ *
+ * @param mediaServer media
+ * @param app app
+ * @param stream 流id
+ */
@Override
public String snapOnPlay(ZlmMediaServer mediaServer, String app, String stream) {
String fileName = app + "-" + stream + ".jpg";
@@ -798,11 +829,53 @@
log.info("[getSnap] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType());
throw new RuntimeException("[getSnap] 失败, mediaServer的类型: " + mediaServer.getType() + ",未找到对应的实现类");
}
- String filePath = fileDomain + filePrefix + "/snap/" + fileName;
- mediaNodeServerService.getSnap(mediaServer, app, stream, 30, 300, this.filePath + "/snap", fileName);
- return filePath;
- }
+ // 生成临时文件路径(用于 FFmpeg 截图)
+ String tempDir = System.getProperty("java.io.tmpdir") + "/snap_temp/";
+ File tempDirFile = new File(tempDir);
+ if (!tempDirFile.exists()) {
+ tempDirFile.mkdirs();
+ }
+ String tempFilePath = tempDir + fileName;
+
+ try {
+ // 1. 获取截图到临时文件
+ mediaNodeServerService.getSnap(mediaServer, app, stream, 30, 300, tempDir, fileName);
+ // 2. 检查临时文件是否存在
+ File tempFile = new File(tempFilePath);
+ if (!tempFile.exists()) {
+ log.error("[截图失败] 临时文件不存在: {}", tempFilePath);
+ return null;
+ }
+
+ // 3. 使用 FileMultipartFile 转换为 MultipartFile
+ MultipartFile multipartFile = new FileMultipartFile(
+ tempFile,
+ fileName,
+ "image/jpeg"
+ );
+
+ // 3. 调用文件上传接口,上传到 MinIO(bucketName 可以固定为 "snap" 或动态传入)
+ R<SysFile> result = remoteFileService.upload(multipartFile, "snap");
+
+ // 4. 清理临时文件
+ Files.deleteIfExists(Paths.get(tempFilePath));
+
+ // 5. 返回 URL
+ if (result.getCode() == Constants.SUCCESS && result.getData() != null) {
+ String url = result.getData().getUrl();
+ log.info("[截图上传成功] URL: {}", url);
+ return url;
+ } else {
+ log.error("[截图上传失败] {}", result.getMsg());
+ return null;
+ }
+
+ } catch (Exception e) {
+ log.error("[截图失败] ", e);
+ return null;
+ }
+ }
/**
* 获取截图
*
diff --git a/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/utils/ZLMRESTfulUtils.java b/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/utils/ZLMRESTfulUtils.java
index 5efa9dd..bd513b8 100644
--- a/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/utils/ZLMRESTfulUtils.java
+++ b/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/utils/ZLMRESTfulUtils.java
@@ -656,7 +656,7 @@
param.put("url", streamUrl);
param.put("timeout_sec", timeout_sec);
param.put("expire_sec", expire_sec);
- param.put("async", 1);
+ param.put("async", 0);
sendGetForImg(mediaServer, "getSnap", param, targetPath, fileName);
}
--
Gitblit v1.9.3