新增抓拍接口。
This commit is contained in:
parent
e8c4af5521
commit
1c0733c915
11
pom.xml
11
pom.xml
@ -217,7 +217,18 @@
|
||||
<artifactId>ruoyi-common</artifactId>
|
||||
<version>${ruoyi.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.8.40</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>com.demo</groupId>
|
||||
<artifactId>dahua-sdk-demo</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
||||
@ -2,16 +2,11 @@ package com.ruoyi.web.controller.system;
|
||||
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.ruoyi.system.service.DahuaSnapService;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import com.ruoyi.common.annotation.Log;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
@ -34,6 +29,8 @@ public class NtzyDeviceInfoController extends BaseController
|
||||
@Autowired
|
||||
private INtzyDeviceInfoService ntzyDeviceInfoService;
|
||||
|
||||
private final DahuaSnapService service =
|
||||
new DahuaSnapService("D:/capture/", "http://192.168.1.96:8080/capture/");
|
||||
/**
|
||||
* 查询设备信息列表
|
||||
*/
|
||||
@ -101,4 +98,20 @@ public class NtzyDeviceInfoController extends BaseController
|
||||
{
|
||||
return toAjax(ntzyDeviceInfoService.deleteNtzyDeviceInfoByIds(ids));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 抓拍成功后保存图片文件
|
||||
* 抓拍成功后
|
||||
* 立即把 JPG 保存到本地
|
||||
* 把“文件访问 URI”返回给调用方——相当于“返回图片链接”效果。
|
||||
*/
|
||||
@GetMapping("/snap")
|
||||
public AjaxResult snap(@RequestParam String ip,
|
||||
@RequestParam(defaultValue = "37777") int port,
|
||||
@RequestParam String user,
|
||||
@RequestParam String pwd){
|
||||
String url = service.snap(ip, port, user, pwd);
|
||||
return success(url);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,103 @@
|
||||
package com.ruoyi.web.controller.system;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import com.ruoyi.common.annotation.Log;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.utils.http.HttpUtils;
|
||||
import com.ruoyi.system.service.INtzyFileService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
|
||||
/**
|
||||
* 录像拍照文件下载Controller
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2025-12-09
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/capture")
|
||||
public class NtzyFileDownloadController extends BaseController
|
||||
{
|
||||
@Autowired
|
||||
private INtzyFileService ntzyFileService;
|
||||
|
||||
/**
|
||||
* 文件下载
|
||||
*/
|
||||
@GetMapping("/**")
|
||||
@Operation(summary = "下载文件")
|
||||
public void getFileContent(HttpServletRequest request, HttpServletResponse response) throws Exception
|
||||
{
|
||||
// 获取请求的路径
|
||||
String path = StrUtil.subAfter(request.getRequestURI(), "/capture/", false);
|
||||
if (StrUtil.isEmpty(path)) {
|
||||
throw new IllegalArgumentException("结尾的 path 路径必须传递");
|
||||
}
|
||||
// 解码,解决中文路径的问题
|
||||
path = URLUtil.decode(path);
|
||||
// 读取内容
|
||||
byte[] content = readFileAsBytes("D:/capture/" + path);
|
||||
if (content == null) {
|
||||
response.setStatus(HttpStatus.NOT_FOUND.value());
|
||||
return;
|
||||
}
|
||||
writeAttachment(response, getFileName(path), content);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回附件
|
||||
*
|
||||
* @param response 响应
|
||||
* @param filename 文件名
|
||||
* @param content 附件内容
|
||||
*/
|
||||
public static void writeAttachment(HttpServletResponse response, String filename, byte[] content) throws IOException {
|
||||
// 设置 header 和 contentType
|
||||
response.setContentType("image/jpeg");
|
||||
// 设置内容显示、下载文件名:https://www.cnblogs.com/wq-9/articles/12165056.html
|
||||
// 参见 https://github.com/YunaiV/ruoyi-vue-pro/issues/692 讨论
|
||||
response.setHeader("Content-Disposition", "inline;filename=" + encode(filename));
|
||||
// 输出附件
|
||||
IoUtil.write(response.getOutputStream(), false, content);
|
||||
}
|
||||
|
||||
public static String encode(String raw) throws UnsupportedEncodingException {
|
||||
return URLEncoder.encode(raw, "utf-8");
|
||||
}
|
||||
|
||||
/**
|
||||
* 从完整路径中提取文件名(含扩展名)
|
||||
* @param path 路径字符串
|
||||
* @return 文件名;若路径为空或仅目录则返回 ""
|
||||
*/
|
||||
public static String getFileName(String path) {
|
||||
if (path == null || path.isEmpty()) return "";
|
||||
return Paths.get(path).getFileName().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取文件并返回 byte[]
|
||||
* @param path 文件路径(相对或绝对)
|
||||
* @return 文件全部字节
|
||||
* @throws IOException 读取失败时抛出
|
||||
*/
|
||||
public static byte[] readFileAsBytes(String path) throws IOException {
|
||||
Path p = Paths.get(path);
|
||||
return Files.readAllBytes(p);
|
||||
}
|
||||
|
||||
}
|
||||
@ -90,6 +90,8 @@ spring:
|
||||
|
||||
# token配置
|
||||
token:
|
||||
enable: false
|
||||
anonymous: true
|
||||
# 令牌自定义标识
|
||||
header: Authorization
|
||||
# 令牌密钥
|
||||
@ -97,6 +99,10 @@ token:
|
||||
# 令牌有效期(默认30分钟)
|
||||
expireTime: 30
|
||||
|
||||
security:
|
||||
excludes:
|
||||
- /capture/**
|
||||
|
||||
# MyBatis配置
|
||||
mybatis:
|
||||
# 搜索指定包别名
|
||||
|
||||
@ -111,7 +111,7 @@ public class SecurityConfig
|
||||
.authorizeHttpRequests((requests) -> {
|
||||
permitAllUrl.getUrls().forEach(url -> requests.antMatchers(url).permitAll());
|
||||
// 对于登录login 注册register 验证码captchaImage 允许匿名访问
|
||||
requests.antMatchers("/login", "/register", "/captchaImage").permitAll()
|
||||
requests.antMatchers("/login", "/register", "/captchaImage", "/capture/**").permitAll()
|
||||
// 静态资源,可匿名访问
|
||||
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
|
||||
.antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
|
||||
@ -120,7 +120,7 @@ public class SecurityConfig
|
||||
})
|
||||
// 添加Logout filter
|
||||
.logout(logout -> logout.logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler))
|
||||
// 添加JWT filter
|
||||
// // 添加JWT filter
|
||||
.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
|
||||
// 添加CORS filter
|
||||
.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class)
|
||||
|
||||
@ -22,7 +22,10 @@
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi-common</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.demo</groupId>
|
||||
<artifactId>dahua-sdk-demo</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@ -0,0 +1,104 @@
|
||||
package com.ruoyi.system.service;
|
||||
|
||||
import com.netsdk.lib.NetSDKLib;
|
||||
import com.sun.jna.Pointer;
|
||||
import com.sun.jna.ptr.IntByReference;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class DahuaSnapService {
|
||||
|
||||
// static {
|
||||
// // 1. 加载大华 SDK
|
||||
// System.loadLibrary("dhnetsdk");
|
||||
// }
|
||||
|
||||
private final NetSDKLib netsdk = NetSDKLib.NETSDK_INSTANCE;
|
||||
private final String saveRoot; // 根目录
|
||||
private final String httpBaseUrl; // nginx 映射前缀
|
||||
|
||||
public DahuaSnapService(String saveRoot, String httpBaseUrl) {
|
||||
this.saveRoot = saveRoot;
|
||||
this.httpBaseUrl = httpBaseUrl;
|
||||
netsdk.CLIENT_Init(null, null); // 初始化
|
||||
}
|
||||
|
||||
/**
|
||||
* 抓拍并返回“图片链接”
|
||||
* @param ip 相机 IP
|
||||
* @param port 37777
|
||||
* @param user admin
|
||||
* @param pwd 密码
|
||||
* @return 可访问的 JPG 地址(http)
|
||||
*/
|
||||
public String snap(String ip, int port, String user, String pwd) {
|
||||
// 2. 登录
|
||||
NetSDKLib.NET_DEVICEINFO_Ex deviceInfo = new NetSDKLib.NET_DEVICEINFO_Ex();
|
||||
IntByReference nError = new IntByReference(0);
|
||||
NetSDKLib.LLong loginId = netsdk.CLIENT_LoginEx2(ip, port, user, pwd,
|
||||
NetSDKLib.EM_LOGIN_SPAC_CAP_TYPE.EM_LOGIN_SPEC_CAP_TCP,
|
||||
null, deviceInfo, nError);
|
||||
if (loginId.longValue() == 0) {
|
||||
throw new RuntimeException("登录失败, error=" + netsdk.CLIENT_GetLastError());
|
||||
}
|
||||
|
||||
// 3. 准备保存路径
|
||||
String cameraId = ip.replace(".", "-");
|
||||
File dir = new File(saveRoot + cameraId);
|
||||
if (!dir.exists()) dir.mkdirs();
|
||||
String fileName = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + ".jpg";
|
||||
String localPath = dir.getAbsolutePath() + File.separator + fileName;
|
||||
|
||||
// 4. 设置抓图回调(一次性,可放构造函数)
|
||||
SnapRevCallBack snapCb = new SnapRevCallBack(localPath);
|
||||
netsdk.CLIENT_SetSnapRevCallBack(snapCb, null);
|
||||
|
||||
// 5. 请求抓拍
|
||||
NetSDKLib.SNAP_PARAMS params = new NetSDKLib.SNAP_PARAMS();
|
||||
params.Channel = 0; // 通道 0
|
||||
params.Quality = 6; // 画质 1-6
|
||||
params.ImageSize = 2; // 0-主码流 2-子码流
|
||||
params.mode = 0;
|
||||
if (!netsdk.CLIENT_SnapPictureEx(loginId, params, null)) {
|
||||
netsdk.CLIENT_Logout(loginId);
|
||||
throw new RuntimeException("抓拍指令失败, error=" + netsdk.CLIENT_GetLastError());
|
||||
}
|
||||
|
||||
// 6. 等待回调写完(简单 CountDownLatch 2s 超时)
|
||||
try { snapCb.latch.await(2, TimeUnit.SECONDS); }
|
||||
catch (InterruptedException e) { Thread.currentThread().interrupt(); }
|
||||
|
||||
// 7. 登出
|
||||
netsdk.CLIENT_Logout(loginId);
|
||||
|
||||
// 8. 返回可访问的 http 链接
|
||||
return httpBaseUrl + cameraId + "/" + fileName;
|
||||
}
|
||||
|
||||
/* ===================== 回调 ===================== */
|
||||
private static class SnapRevCallBack implements NetSDKLib.fSnapRev {
|
||||
private final String savePath;
|
||||
private final CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
SnapRevCallBack(String savePath) { this.savePath = savePath; }
|
||||
|
||||
@Override
|
||||
public void invoke(NetSDKLib.LLong lLoginID, Pointer pBuf, int revLen,
|
||||
int encodeType, int cmdSerial, Pointer dwUser) {
|
||||
if (pBuf == null || revLen <= 0) return;
|
||||
// encodeType == 10 → JPG
|
||||
try (FileOutputStream fos = new FileOutputStream(savePath)) {
|
||||
fos.write(pBuf.getByteArray(0, revLen));
|
||||
latch.countDown();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
ruoyi-system/src/main/resources/lib/netsdk-1.0-demo.jar
Normal file
BIN
ruoyi-system/src/main/resources/lib/netsdk-1.0-demo.jar
Normal file
Binary file not shown.
BIN
ruoyi-system/src/main/resources/lib/win-x64/ImageAlg.dll
Normal file
BIN
ruoyi-system/src/main/resources/lib/win-x64/ImageAlg.dll
Normal file
Binary file not shown.
BIN
ruoyi-system/src/main/resources/lib/win-x64/Infra.dll
Normal file
BIN
ruoyi-system/src/main/resources/lib/win-x64/Infra.dll
Normal file
Binary file not shown.
BIN
ruoyi-system/src/main/resources/lib/win-x64/IvsDrawer.dll
Normal file
BIN
ruoyi-system/src/main/resources/lib/win-x64/IvsDrawer.dll
Normal file
Binary file not shown.
BIN
ruoyi-system/src/main/resources/lib/win-x64/StreamConvertor.dll
Normal file
BIN
ruoyi-system/src/main/resources/lib/win-x64/StreamConvertor.dll
Normal file
Binary file not shown.
BIN
ruoyi-system/src/main/resources/lib/win-x64/avnetsdk.dll
Normal file
BIN
ruoyi-system/src/main/resources/lib/win-x64/avnetsdk.dll
Normal file
Binary file not shown.
BIN
ruoyi-system/src/main/resources/lib/win-x64/dhconfigsdk.dll
Normal file
BIN
ruoyi-system/src/main/resources/lib/win-x64/dhconfigsdk.dll
Normal file
Binary file not shown.
BIN
ruoyi-system/src/main/resources/lib/win-x64/dhnetsdk.dll
Normal file
BIN
ruoyi-system/src/main/resources/lib/win-x64/dhnetsdk.dll
Normal file
Binary file not shown.
BIN
ruoyi-system/src/main/resources/lib/win-x64/libeay32.dll
Normal file
BIN
ruoyi-system/src/main/resources/lib/win-x64/libeay32.dll
Normal file
Binary file not shown.
BIN
ruoyi-system/src/main/resources/lib/win-x64/play.dll
Normal file
BIN
ruoyi-system/src/main/resources/lib/win-x64/play.dll
Normal file
Binary file not shown.
BIN
ruoyi-system/src/main/resources/lib/win-x64/ssleay32.dll
Normal file
BIN
ruoyi-system/src/main/resources/lib/win-x64/ssleay32.dll
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user