Browse Source

fix:通过 NVR SDK 抓图,创建文件权限不足问题

master
wangguangyuan 1 month ago
parent
commit
0d1e0da84c
4 changed files with 53 additions and 15 deletions
  1. +2
    -2
      Dockerfile
  2. +4
    -0
      docker-compose.yml
  3. +2
    -2
      src/main/java/com/inspect/nvr/hik/service/HikNvrLiveStreamService.java
  4. +45
    -11
      src/main/java/com/inspect/nvr/service/CommonCameraService.java

+ 2
- 2
Dockerfile View File

@ -90,8 +90,8 @@ COPY --from=ffmpeg /ffprobe /usr/bin/ffprobe
RUN groupadd --gid 10001 inspect-nvr \ RUN groupadd --gid 10001 inspect-nvr \
&& useradd --uid 10001 --gid inspect-nvr --home-dir /app --shell /usr/sbin/nologin inspect-nvr \ && useradd --uid 10001 --gid inspect-nvr --home-dir /app --shell /usr/sbin/nologin inspect-nvr \
&& mkdir -p /app/lib/HCNetSDKCom /app/lib/linux64 \ && mkdir -p /app/lib/HCNetSDKCom /app/lib/linux64 \
/app/images /app/sdkLog /app/sdklog /app/target /data/logs/inspect-nvr \
&& chown -R inspect-nvr:inspect-nvr /app /data/logs
/app/images /app/sdkLog /app/sdklog /app/target /data/captures /data/logs/inspect-nvr \
&& chown -R inspect-nvr:inspect-nvr /app /data/captures /data/logs
WORKDIR /app WORKDIR /app


+ 4
- 0
docker-compose.yml View File

@ -16,6 +16,8 @@ services:
TZ: ${TZ:-Asia/Shanghai} TZ: ${TZ:-Asia/Shanghai}
SPRING_PROFILES_ACTIVE: docker SPRING_PROFILES_ACTIVE: docker
SERVER_PORT: 8081 SERVER_PORT: 8081
# 抓图写入独立持久卷,避免应用目录权限或容器重建导致抓图失败/丢失。
INSPECT_NVR_CAPTURE_DIR: /data/captures
DB_URL: ${DB_URL:?DB_URL_required_in_.env} DB_URL: ${DB_URL:?DB_URL_required_in_.env}
DB_USERNAME: ${DB_USERNAME:?DB_USERNAME_required_in_.env} DB_USERNAME: ${DB_USERNAME:?DB_USERNAME_required_in_.env}
DB_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD_required_in_.env} DB_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD_required_in_.env}
@ -53,6 +55,7 @@ services:
- app_dahua_sdk_logs:/app/sdklog - app_dahua_sdk_logs:/app/sdklog
- app_images:/app/images - app_images:/app/images
- app_runtime:/app/target - app_runtime:/app/target
- app_captures:/data/captures
networks: networks:
- inspect-nvr - inspect-nvr
@ -66,3 +69,4 @@ volumes:
app_dahua_sdk_logs: app_dahua_sdk_logs:
app_images: app_images:
app_runtime: app_runtime:
app_captures:

+ 2
- 2
src/main/java/com/inspect/nvr/hik/service/HikNvrLiveStreamService.java View File

@ -1872,12 +1872,12 @@ public class HikNvrLiveStreamService {
/** 返回当前流的 HLS 浏览器地址。 */ /** 返回当前流的 HLS 浏览器地址。 */
private String hlsUrl(String streamId, String baseUrl) { private String hlsUrl(String streamId, String baseUrl) {
return baseUrl + "/live/" + streamId + "/hls.m3u8";
return "/live/" + streamId + "/hls.m3u8";
} }
/** 返回当前流的 HTTP-FLV 浏览器地址。 */ /** 返回当前流的 HTTP-FLV 浏览器地址。 */
private String flvUrl(String streamId, String baseUrl) { private String flvUrl(String streamId, String baseUrl) {
return baseUrl + "/live/" + streamId + ".live.flv";
return "/live/" + streamId + ".live.flv";
} }
/** /**


+ 45
- 11
src/main/java/com/inspect/nvr/service/CommonCameraService.java View File

@ -15,6 +15,7 @@ import org.springframework.util.StreamUtils;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
@ -33,7 +34,7 @@ import java.util.concurrent.locks.ReentrantLock;
public class CommonCameraService { public class CommonCameraService {
/** 摄像机抓图失败时写入的兜底图片路径。 */ /** 摄像机抓图失败时写入的兜底图片路径。 */
static final String IMAGE_CAPTURE_FAILED = "images\\imageCaptureFailed.jpg"; static final String IMAGE_CAPTURE_FAILED = "images\\imageCaptureFailed.jpg";
/** 抓图保存根目录,由当前操作系统和运行目录决定。 */
/** 抓图保存根目录,可由 INSPECT_NVR_CAPTURE_DIR 指定。 */
public static final String FILE_DIR = determineCaptureDirectory(); public static final String FILE_DIR = determineCaptureDirectory();
/** 厂商 SDK 抓图失败时的默认最大重试次数。 */ /** 厂商 SDK 抓图失败时的默认最大重试次数。 */
static final int DEFAULT_MAX_RETRIES = 10; static final int DEFAULT_MAX_RETRIES = 10;
@ -201,8 +202,10 @@ public class CommonCameraService {
Path getFullPath(String flag, String ip, int channel) { Path getFullPath(String flag, String ip, int channel) {
String timeStr = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss_SSS")); String timeStr = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss_SSS"));
String fileName = flag + "_" + ip + "_" + channel + "_" + timeStr + ".jpg"; String fileName = flag + "_" + ip + "_" + channel + "_" + timeStr + ".jpg";
String saveDir = FILE_DIR + LocalDate.now();
Path fullPath = Paths.get(saveDir, fileName).toAbsolutePath();
Path fullPath = Paths.get(FILE_DIR)
.resolve(LocalDate.now().toString())
.resolve(fileName)
.toAbsolutePath();
ensureDirectoryExists(fullPath.getParent()); ensureDirectoryExists(fullPath.getParent());
return fullPath; return fullPath;
} }
@ -212,16 +215,47 @@ public class CommonCameraService {
* 注意路径避免包含中文,否则可能会导致文件保存失败或乱码 * 注意路径避免包含中文,否则可能会导致文件保存失败或乱码
*/ */
private static String determineCaptureDirectory() { private static String determineCaptureDirectory() {
String os = System.getProperty("os.name").toLowerCase();
log.info("当前操作系统: {}", os);
String userDir = System.getProperty("user.dir");
log.info("userDir: {}", userDir);
String configuredDirectory = System.getenv("INSPECT_NVR_CAPTURE_DIR");
if (configuredDirectory == null || configuredDirectory.trim().isEmpty()) {
configuredDirectory = System.getProperty("inspect.nvr.capture-dir");
}
if (os.contains("win")) {
return "D:/captures/";
Path preferredDirectory;
if (configuredDirectory != null && !configuredDirectory.trim().isEmpty()) {
preferredDirectory = Paths.get(configuredDirectory.trim()).toAbsolutePath();
} else if (System.getProperty("os.name", "").toLowerCase().contains("win")) {
preferredDirectory = Paths.get("D:/captures").toAbsolutePath();
} else { } else {
// Linux/Unix/Mac 系统使用项目同级目录
return userDir + "/captures/";
preferredDirectory = Paths.get(System.getProperty("user.dir"), "captures").toAbsolutePath();
}
try {
ensureWritableDirectory(preferredDirectory);
log.info("抓图目录已就绪: {}", preferredDirectory);
return preferredDirectory.toString() + java.io.File.separator;
} catch (IOException primaryFailure) {
Path fallbackDirectory = Paths.get(System.getProperty("java.io.tmpdir"), "inspect-nvr-captures")
.toAbsolutePath();
try {
ensureWritableDirectory(fallbackDirectory);
log.warn("抓图目录 {} 不可写,已回退到 {}:{}",
preferredDirectory, fallbackDirectory, primaryFailure.toString());
return fallbackDirectory.toString() + java.io.File.separator;
} catch (IOException fallbackFailure) {
fallbackFailure.addSuppressed(primaryFailure);
throw new IllegalStateException("抓图目录不可写,主目录=" + preferredDirectory
+ ",临时目录=" + fallbackDirectory, fallbackFailure);
}
}
}
/** 创建目录并通过真实文件写入确认运行用户拥有写权限。 */
private static void ensureWritableDirectory(Path directory) throws IOException {
Files.createDirectories(directory);
if (!Files.isDirectory(directory)) {
throw new FileAlreadyExistsException(directory.toString(), null, "目标路径不是目录");
} }
Path probe = Files.createTempFile(directory, ".inspect-nvr-write-check-", ".tmp");
Files.delete(probe);
} }
} }

Loading…
Cancel
Save