Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
exiftool_test
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
guokunkun
exiftool_test
Commits
32500dae
Commit
32500dae
authored
Jul 28, 2026
by
kk
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
初建项目
parent
0d52c19c
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
99 additions
and
0 deletions
+99
-0
README.md
README.md
+18
-0
ExifTool.java
src/main/java/com/example/exiftool/ExifTool.java
+81
-0
No files found.
README.md
View file @
32500dae
...
...
@@ -41,3 +41,21 @@ ExifToolResult r = exiftool.addSubject("333.jpg", "contains-synthetic-performer"
ExifToolResult
v
=
exiftool
.
execute
(
"-ver"
);
if
(!
v
.
isSuccess
())
System
.
err
.
println
(
v
.
stderr
());
```
## 批量处理
给一批文件打
**相同**
标签/参数,只启动
**一个**
exiftool 进程(exiftool 冷启动慢,批量务必走这里,不要循环调
`addSubject`
):
```
java
List
<
String
>
files
=
Arrays
.
asList
(
"a.jpg"
,
"b.jpg"
,
"c.jpg"
);
// 便捷:批量追加同一个 Subject
ExifToolResult
r
=
exiftool
.
addSubjectBatch
(
files
,
"contains-synthetic-performer"
);
// 通用:对每个文件应用相同选项
ExifToolResult
r2
=
exiftool
.
executeBatch
(
Arrays
.
asList
(
"-overwrite_original"
,
"-XMP-dc:Subject+=foo"
),
files
);
```
文件数量大导致命令行超长时,内部自动改用 exiftool 的
`-@ 参数文件`
(临时文件, UTF-8),规避 Windows/Linux 命令行长度上限,调用方无感知。返回单个
`ExifToolResult`
,任一文件出错时
`isSuccess()`
为 false、
`stderr()`
含逐文件错误。
src/main/java/com/example/exiftool/ExifTool.java
View file @
32500dae
...
...
@@ -42,6 +42,9 @@ public class ExifTool {
private
static
final
String
RESOURCE_ROOT
=
"exiftool"
;
/** 批量参数拼成命令行的长度阈值,超过则改用 -@ 参数文件,规避 OS 命令行长度上限。 */
private
static
final
int
ARG_LINE_THRESHOLD
=
30000
;
/** 缓存解析出的可执行文件路径,懒加载,双检锁保证线程安全。 */
private
static
volatile
Path
resolvedExecutable
;
...
...
@@ -112,6 +115,84 @@ public class ExifTool {
return
Arrays
.
asList
(
"-overwrite_original"
,
"-XMP-dc:Subject+="
+
value
,
file
);
}
// ----- 批量处理(方案 A:一条命令处理多文件,仅 1 个进程)-----
/**
* 批量执行:对一批文件应用<b>相同</b>的 exiftool 选项,单个进程处理全部。
*
* <p>参数量小时直接走命令行;拼接长度超过 {@link #ARG_LINE_THRESHOLD} 时
* 自动改用 exiftool 的 {@code -@ 参数文件}(临时文件, UTF-8),规避
* Windows/Linux 的命令行长度上限。
*
* @param options 应用到每个文件的选项,如 {@code ["-overwrite_original", "-XMP-dc:Subject+=foo"]}
* @param files 文件路径列表,不能为空
* @return 单次执行结果;任一文件出错时 exitCode 非零,stderr 含逐文件错误信息
*/
public
ExifToolResult
executeBatch
(
List
<
String
>
options
,
List
<
String
>
files
)
{
List
<
String
>
combined
=
combineBatch
(
options
,
files
);
long
approxLen
=
0
;
for
(
String
s
:
combined
)
{
approxLen
+=
s
.
length
()
+
3
;
// 预留引号/分隔符
}
if
(
approxLen
>
ARG_LINE_THRESHOLD
)
{
return
executeViaArgFile
(
combined
);
}
return
execute
(
combined
);
}
/**
* 便捷方法:给一批文件的 XMP-dc:Subject 追加<b>同一个</b>值,单进程完成。
* 等价于 {@code exiftool -overwrite_original -XMP-dc:Subject+=<value> f1 f2 ...}。
*/
public
ExifToolResult
addSubjectBatch
(
List
<
String
>
files
,
String
value
)
{
return
executeBatch
(
Arrays
.
asList
(
"-overwrite_original"
,
"-XMP-dc:Subject+="
+
value
),
files
);
}
/** 合并选项与文件为一个参数列表(抽出以便单测);files 为空时抛异常。 */
static
List
<
String
>
combineBatch
(
List
<
String
>
options
,
List
<
String
>
files
)
{
if
(
files
==
null
||
files
.
isEmpty
())
{
throw
new
IllegalArgumentException
(
"批量文件列表不能为空"
);
}
List
<
String
>
combined
=
new
ArrayList
<
String
>();
if
(
options
!=
null
)
{
combined
.
addAll
(
options
);
}
combined
.
addAll
(
files
);
return
combined
;
}
/** 把参数写入临时 -@ 参数文件后执行,用完删除。 */
private
ExifToolResult
executeViaArgFile
(
List
<
String
>
args
)
{
Path
argFile
=
null
;
try
{
argFile
=
Files
.
createTempFile
(
"exiftool-args-"
,
".txt"
);
writeArgFile
(
argFile
,
args
);
return
execute
(
Arrays
.
asList
(
"-@"
,
argFile
.
toString
()));
}
catch
(
IOException
e
)
{
throw
new
ExifToolException
(
"写入 exiftool 参数文件失败"
,
e
);
}
finally
{
if
(
argFile
!=
null
)
{
try
{
Files
.
deleteIfExists
(
argFile
);
}
catch
(
IOException
ignored
)
{
// 临时文件删除失败不影响结果。
}
}
}
}
/** 每个参数一行写入文件(UTF-8),供 exiftool -@ 读取。 */
static
void
writeArgFile
(
Path
file
,
List
<
String
>
args
)
throws
IOException
{
StringBuilder
sb
=
new
StringBuilder
();
for
(
String
a
:
args
)
{
sb
.
append
(
a
).
append
(
'\n'
);
}
Files
.
write
(
file
,
sb
.
toString
().
getBytes
(
StandardCharsets
.
UTF_8
));
}
// ----- 可执行文件解析与释放 -----
/** 返回(必要时先释放并缓存)当前平台的 exiftool 可执行文件路径。 */
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment