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