Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
Y
YX_IDENT_beijing_auxiliary_YD
Project
Project
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
zhangyusheng
YX_IDENT_beijing_auxiliary_YD
Commits
09158a24
Commit
09158a24
authored
Jun 11, 2026
by
xiachenqi
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
调整快递单文字样式。优化港澳台购车人分析业务代码。
parent
6e958ec3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
108 additions
and
11 deletions
+108
-11
ExtensionApi.java
src/main/java/com/yxproject/start/api/ExtensionApi.java
+22
-2
ExtensionServiceImpl.java
...om/yxproject/start/service/impl/ExtensionServiceImpl.java
+85
-8
searchCardMsg.html
...n/resources/static/views/searchCardMsg/searchCardMsg.html
+1
-1
No files found.
src/main/java/com/yxproject/start/api/ExtensionApi.java
View file @
09158a24
...
...
@@ -8,6 +8,7 @@ import org.springframework.web.bind.annotation.RestController;
import
org.springframework.web.multipart.MultipartFile
;
import
javax.ws.rs.core.MediaType
;
import
java.io.IOException
;
/**
...
...
@@ -40,7 +41,18 @@ public class ExtensionApi {
if
(
fileName
!=
null
&&
!
fileName
.
endsWith
(
"xls"
)
&&
!
fileName
.
endsWith
(
"xlsx"
))
{
throw
new
RuntimeException
(
"Only .xls or .xlsx files are supported"
);
}
Thread
thread
=
new
Thread
(()
->
extensionService
.
analysisGAT
(
file
));
// 必须在请求线程内提前读取文件字节!
// MultipartFile 底层资源依附于 HTTP 请求生命周期,
// 若直接将 file 传入新线程,请求结束后资源被释放,
// 子线程读到损坏数据会触发 "Invalid byte of UTF-8 sequence" 异常
final
byte
[]
fileBytes
;
final
String
originalFileName
=
fileName
;
try
{
fileBytes
=
file
.
getBytes
();
}
catch
(
IOException
e
)
{
throw
new
RuntimeException
(
"Failed to read uploaded file"
,
e
);
}
Thread
thread
=
new
Thread
(()
->
extensionService
.
analysisGAT
(
fileBytes
,
originalFileName
));
// 处理文件并生成新的Excel
thread
.
start
();
return
"ok"
;
...
...
@@ -58,7 +70,15 @@ public class ExtensionApi {
if
(
fileName
!=
null
&&
!
fileName
.
endsWith
(
"xls"
)
&&
!
fileName
.
endsWith
(
"xlsx"
))
{
throw
new
RuntimeException
(
"Only .xls or .xlsx files are supported"
);
}
Thread
thread
=
new
Thread
(()
->
extensionService
.
analysisGAT
(
file
));
// 同样在请求线程内提前读取字节,避免子线程读到已释放的资源
final
byte
[]
fileBytes
;
final
String
originalFileName
=
fileName
;
try
{
fileBytes
=
file
.
getBytes
();
}
catch
(
IOException
e
)
{
throw
new
RuntimeException
(
"Failed to read uploaded file: "
+
fileName
,
e
);
}
Thread
thread
=
new
Thread
(()
->
extensionService
.
analysisGAT
(
fileBytes
,
originalFileName
));
// 处理文件并生成新的Excel
thread
.
start
();
}
...
...
src/main/java/com/yxproject/start/service/impl/ExtensionServiceImpl.java
View file @
09158a24
...
...
@@ -5,12 +5,18 @@ import com.yxproject.start.dto.ExtensionGatDto;
import
com.yxproject.start.mapper.ExtensionMapper
;
import
org.apache.poi.ss.usermodel.*
;
import
org.springframework.stereotype.Service
;
import
org.springframework.web.multipart.MultipartFile
;
import
java.io.*
;
import
java.nio.file.Files
;
import
java.nio.ByteBuffer
;
import
java.nio.CharBuffer
;
import
java.nio.charset.CharsetDecoder
;
import
java.nio.charset.CodingErrorAction
;
import
java.nio.charset.StandardCharsets
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.zip.ZipEntry
;
import
java.util.zip.ZipInputStream
;
import
java.util.zip.ZipOutputStream
;
/**
* 扩展服务impl
...
...
@@ -30,26 +36,97 @@ public class ExtensionServiceImpl {
/**
* 解析港澳台购车人信息,判断是否办理过港澳台居住证
*
* @param file 文件
* @param fileBytes 文件字节(必须在请求线程中提前读取,避免子线程中 MultipartFile 资源已释放)
* @param fileName 原始文件名
*/
public
void
analysisGAT
(
MultipartFile
file
)
{
try
(
InputStream
inputStream
=
file
.
getInputStream
();
public
void
analysisGAT
(
byte
[]
fileBytes
,
String
fileName
)
{
System
.
out
.
println
(
"start"
);
try
{
// xlsx 本质是 ZIP,部分 WPS 生成的文件内部 XML(如 styles.xml)包含
// 非标准 UTF-8 字节序列,需要在交给 POI 解析前先修复编码
byte
[]
fixedBytes
=
fixXlsxEncoding
(
fileBytes
);
try
(
InputStream
inputStream
=
new
ByteArrayInputStream
(
fixedBytes
);
Workbook
workbook
=
WorkbookFactory
.
create
(
inputStream
))
{
Sheet
hkSheet
=
workbook
.
getSheetAt
(
0
);
// 分析和更新Sheet
analysisSheet
(
hkSheet
);
String
fileName
=
file
.
getOriginalFilename
();
File
excelFile
=
new
File
(
"./"
+
"(已比对)"
+
fileName
);
try
(
OutputStream
outputStream
=
Files
.
newOutputStream
(
excelFile
.
toPath
()))
{
File
excelFile
=
new
File
(
"./"
+
"(modified)"
+
fileName
);
// 使用 FileOutputStream 而非 Files.newOutputStream(file.toPath())
// 因为 toPath() 内部调用 UnixPath.encode() 依赖 JVM 默认字符集,
// 当系统默认字符集非 UTF-8 时,中文路径会触发 InvalidPathException
try
(
OutputStream
outputStream
=
new
FileOutputStream
(
excelFile
))
{
workbook
.
write
(
outputStream
);
outputStream
.
flush
();
}
}
}
catch
(
IOException
e
)
{
throw
new
RuntimeException
(
e
);
}
System
.
out
.
println
(
"比对完成"
);
}
/**
* 修复 xlsx(ZIP)内部 XML 文件中的非法 UTF-8 字节序列。
* WPS 等工具有时将 GBK 编码的字体名写入声明为 UTF-8 的 XML,
* 导致 Apache POI / Xerces 解析时抛出 MalformedByteSequenceException。
* 此方法将非法字节替换为 UTF-8 替换字符(U+FFFD),保证文件可被正常解析。
*/
private
byte
[]
fixXlsxEncoding
(
byte
[]
fileBytes
)
throws
IOException
{
ByteArrayOutputStream
out
=
new
ByteArrayOutputStream
();
try
(
ZipInputStream
zis
=
new
ZipInputStream
(
new
ByteArrayInputStream
(
fileBytes
));
ZipOutputStream
zos
=
new
ZipOutputStream
(
out
))
{
ZipEntry
entry
;
while
((
entry
=
zis
.
getNextEntry
())
!=
null
)
{
zos
.
putNextEntry
(
new
ZipEntry
(
entry
.
getName
()));
String
name
=
entry
.
getName
();
if
(
name
.
endsWith
(
".xml"
)
||
name
.
endsWith
(
".rels"
))
{
// 读取 XML 原始字节,用宽容解码器修复非法 UTF-8 序列后重新写入
byte
[]
raw
=
readEntryBytes
(
zis
);
zos
.
write
(
fixUtf8Bytes
(
raw
));
}
else
{
// 非 XML 条目(图片、二进制等)原样复制
byte
[]
buf
=
new
byte
[
8192
];
int
len
;
while
((
len
=
zis
.
read
(
buf
))
>
0
)
{
zos
.
write
(
buf
,
0
,
len
);
}
}
zos
.
closeEntry
();
zis
.
closeEntry
();
}
}
return
out
.
toByteArray
();
}
/**
* 读取 ZipInputStream 当前条目的全部字节(不关闭流)。
*/
private
byte
[]
readEntryBytes
(
ZipInputStream
zis
)
throws
IOException
{
ByteArrayOutputStream
buffer
=
new
ByteArrayOutputStream
();
byte
[]
tmp
=
new
byte
[
8192
];
int
len
;
while
((
len
=
zis
.
read
(
tmp
))
>
0
)
{
buffer
.
write
(
tmp
,
0
,
len
);
}
return
buffer
.
toByteArray
();
}
/**
* 使用宽容 UTF-8 解码器将非法字节替换为替换字符,再重新编码为合法 UTF-8 字节。
*/
private
byte
[]
fixUtf8Bytes
(
byte
[]
raw
)
{
CharsetDecoder
decoder
=
StandardCharsets
.
UTF_8
.
newDecoder
()
.
onMalformedInput
(
CodingErrorAction
.
REPLACE
)
.
onUnmappableCharacter
(
CodingErrorAction
.
REPLACE
);
try
{
CharBuffer
chars
=
decoder
.
decode
(
ByteBuffer
.
wrap
(
raw
));
return
chars
.
toString
().
getBytes
(
StandardCharsets
.
UTF_8
);
}
catch
(
Exception
e
)
{
// 极端情况下解码仍失败,原样返回
return
raw
;
}
}
/**
* 分析表
*
...
...
src/main/resources/static/views/searchCardMsg/searchCardMsg.html
View file @
09158a24
...
...
@@ -119,7 +119,7 @@
<table
cellspacing=
"0"
cellpadding=
"0"
style=
"border-collapse:collapse;height:24mm"
>
<tr>
<td
style=
"width:72mm;height:4mm"
>
<span
style=
"font-size:4mm; position: absolute;top:1mm;left:
55mm;font-family:'黑体';"
>
特快专递
</span>
<span
style=
"font-size:4mm; position: absolute;top:1mm;left:
45mm;font-family:'黑体';"
>
特快专递-特安
</span>
</td>
</tr>
<tr>
...
...
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