|
|
@ -1,8 +1,12 @@ |
|
|
package com.ruoyi.web.controller.system; |
|
|
package com.ruoyi.web.controller.system; |
|
|
|
|
|
|
|
|
import java.util.HashMap; |
|
|
import java.io.BufferedReader; |
|
|
import java.util.List; |
|
|
import java.io.IOException; |
|
|
import java.util.Map; |
|
|
import java.io.InputStream; |
|
|
|
|
|
import java.io.InputStreamReader; |
|
|
|
|
|
import java.nio.charset.Charset; |
|
|
|
|
|
import java.nio.charset.StandardCharsets; |
|
|
|
|
|
import java.util.*; |
|
|
import javax.servlet.http.HttpServletRequest; |
|
|
import javax.servlet.http.HttpServletRequest; |
|
|
import javax.servlet.http.HttpServletResponse; |
|
|
import javax.servlet.http.HttpServletResponse; |
|
|
|
|
|
|
|
|
@ -69,14 +73,172 @@ public class ZhyPointController extends BaseController |
|
|
if (fileName == null || !fileName.toLowerCase().endsWith(".csv")) { |
|
|
if (fileName == null || !fileName.toLowerCase().endsWith(".csv")) { |
|
|
return AjaxResult.error("仅支持上传 CSV 格式的文件"); |
|
|
return AjaxResult.error("仅支持上传 CSV 格式的文件"); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 3. 自动检测编码并读取 CSV 文件(支持 GBK / UTF-8)
|
|
|
|
|
|
List<Map<String, String>> dataList = new ArrayList<>(); |
|
|
|
|
|
String[] headers = null; |
|
|
|
|
|
|
|
|
|
|
|
InputStream inputStream = null; |
|
|
|
|
|
BufferedReader reader = null; |
|
|
|
|
|
try { |
|
|
|
|
|
inputStream = file.getInputStream(); |
|
|
|
|
|
|
|
|
|
|
|
// 🔍 第一步:探测编码
|
|
|
|
|
|
String encoding = detectCsvEncoding(inputStream); |
|
|
|
|
|
System.out.println("自动检测到编码: " + encoding); |
|
|
|
|
|
|
|
|
|
|
|
// 重新打开流(因为探测时已读取部分数据)
|
|
|
|
|
|
inputStream.close(); |
|
|
|
|
|
inputStream = file.getInputStream(); |
|
|
|
|
|
|
|
|
|
|
|
reader = new BufferedReader(new InputStreamReader(inputStream, encoding)); |
|
|
|
|
|
|
|
|
|
|
|
String line; |
|
|
|
|
|
int lineNumber = 0; |
|
|
|
|
|
|
|
|
|
|
|
while ((line = reader.readLine()) != null) { |
|
|
|
|
|
lineNumber++; |
|
|
|
|
|
|
|
|
|
|
|
// 去除 BOM(如果存在)
|
|
|
|
|
|
if (lineNumber == 1 && line.startsWith("\uFEFF")) { |
|
|
|
|
|
line = line.substring(1); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// ✅ 替换 split(",") 为能处理引号内逗号的解析方法
|
|
|
|
|
|
String[] row = parseCsvLine(line); |
|
|
|
|
|
for (int i = 0; i < row.length; i++) { |
|
|
|
|
|
row[i] = row[i].trim(); // 去空格
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (lineNumber == 1) { |
|
|
|
|
|
// 第一行是表头
|
|
|
|
|
|
headers = row; |
|
|
|
|
|
continue; // 跳过第一行,不加入数据
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 将每一行转为 Map<String, String>
|
|
|
|
|
|
Map<String, String> rowMap = new HashMap<>(); |
|
|
|
|
|
for (int i = 0; i < headers.length && i < row.length; i++) { |
|
|
|
|
|
String key = headers[i].trim(); |
|
|
|
|
|
// 只保留你需要的字段
|
|
|
|
|
|
if (Arrays.asList("ck", "main_keyword", "sub_keyword", "title", "authors", "keywords", "abstract", "url", "urls").contains(key)) { |
|
|
|
|
|
rowMap.put(key, row[i]); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
dataList.add(rowMap); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} catch (IOException e) { |
|
|
|
|
|
return AjaxResult.error("读取文件时发生错误:" + e.getMessage()); |
|
|
|
|
|
} finally { |
|
|
|
|
|
try { |
|
|
|
|
|
if (reader != null) reader.close(); |
|
|
|
|
|
if (inputStream != null) inputStream.close(); |
|
|
|
|
|
} catch (IOException e) { |
|
|
|
|
|
e.printStackTrace(); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pointService.buildPoint(dataList); |
|
|
|
|
|
|
|
|
return AjaxResult.success(); |
|
|
return AjaxResult.success(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private String[] parseCsvLine(String line) { |
|
|
|
|
|
List<String> result = new ArrayList<>(); |
|
|
|
|
|
StringBuilder current = new StringBuilder(); |
|
|
|
|
|
boolean inQuotes = false; |
|
|
|
|
|
char[] chars = line.toCharArray(); |
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < chars.length; i++) { |
|
|
|
|
|
char c = chars[i]; |
|
|
|
|
|
|
|
|
|
|
|
if (c == '"') { |
|
|
|
|
|
// 处理转义引号:"" -> "
|
|
|
|
|
|
if (i + 1 < chars.length && chars[i + 1] == '"') { |
|
|
|
|
|
current.append('"'); |
|
|
|
|
|
i++; // 跳过下一个引号
|
|
|
|
|
|
} else { |
|
|
|
|
|
inQuotes = !inQuotes; |
|
|
|
|
|
} |
|
|
|
|
|
} else if (c == ',' && !inQuotes) { |
|
|
|
|
|
// 只有不在引号内时,逗号才是分隔符
|
|
|
|
|
|
result.add(current.toString()); |
|
|
|
|
|
current.setLength(0); // 清空
|
|
|
|
|
|
} else { |
|
|
|
|
|
current.append(c); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 添加最后一列
|
|
|
|
|
|
result.add(current.toString()); |
|
|
|
|
|
return result.toArray(new String[0]); |
|
|
|
|
|
} |
|
|
/** |
|
|
/** |
|
|
* 查询知识点管理列表 |
|
|
* 查询知识点管理列表 |
|
|
*/ |
|
|
*/ |
|
|
|
|
|
private String detectCsvEncoding(InputStream inputStream) throws IOException { |
|
|
|
|
|
byte[] buf = new byte[4096]; |
|
|
|
|
|
int readSize = inputStream.read(buf); |
|
|
|
|
|
|
|
|
|
|
|
// 如果文件很小,只读一部分也够了
|
|
|
|
|
|
int size = readSize > 0 ? readSize : buf.length; |
|
|
|
|
|
|
|
|
|
|
|
// 1. 检查是否以 UTF-8 BOM 开头
|
|
|
|
|
|
if (size >= 3 && buf[0] == (byte)0xEF && buf[1] == (byte)0xBB && buf[2] == (byte)0xBF) { |
|
|
|
|
|
return "UTF-8"; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 2. 使用简单的启发式判断 GBK vs UTF-8
|
|
|
|
|
|
boolean isDefinitelyUtf8 = false; |
|
|
|
|
|
boolean isProbablyGbk = false; |
|
|
|
|
|
|
|
|
|
|
|
int i = 0; |
|
|
|
|
|
while (i < size) { |
|
|
|
|
|
byte b = buf[i]; |
|
|
|
|
|
|
|
|
|
|
|
if (b >= 0) { |
|
|
|
|
|
// ASCII 字符,两种编码都一样
|
|
|
|
|
|
i++; |
|
|
|
|
|
continue; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 存在负数 -> 多字节字符
|
|
|
|
|
|
if ((b & 0xE0) == 0xC0 && i + 1 < size) { |
|
|
|
|
|
// 2字节 UTF-8
|
|
|
|
|
|
i += 2; |
|
|
|
|
|
} else if ((b & 0xF0) == 0xE0 && i + 2 < size) { |
|
|
|
|
|
// 3字节 UTF-8
|
|
|
|
|
|
i += 3; |
|
|
|
|
|
} else if ((b & 0xF8) == 0xF0 && i + 3 < size) { |
|
|
|
|
|
// 4字节 UTF-8
|
|
|
|
|
|
i += 4; |
|
|
|
|
|
} else { |
|
|
|
|
|
// 不符合 UTF-8 规则,很可能是 GBK
|
|
|
|
|
|
isProbablyGbk = true; |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 如果符合 UTF-8 编码规则,并且包含中文(常见于 UTF-8 文件),优先认为是 UTF-8
|
|
|
|
|
|
String sampleStr = new String(buf, 0, size, StandardCharsets.UTF_8); |
|
|
|
|
|
if (!isProbablyGbk && sampleStr.matches(".*[\\u4e00-\\u9fa5]+.*")) { |
|
|
|
|
|
isDefinitelyUtf8 = true; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 启发式判断:如果字符串用 UTF-8 解出来是乱码,而用 GBK 解出来正常,则是 GBK
|
|
|
|
|
|
String utf8Str = new String(buf, 0, size, StandardCharsets.UTF_8); |
|
|
|
|
|
String gbkStr = new String(buf, 0, size, Charset.forName("GBK")); |
|
|
|
|
|
|
|
|
|
|
|
// 简单判断:GBK 解码后是否出现 (替换字符)
|
|
|
|
|
|
if (utf8Str.contains("") || utf8Str.length() != gbkStr.length()) { |
|
|
|
|
|
// UTF-8 出现乱码,说明更可能是 GBK
|
|
|
|
|
|
return "GBK"; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 默认 fallback
|
|
|
|
|
|
return isDefinitelyUtf8 ? "UTF-8" : "GBK"; |
|
|
|
|
|
} |
|
|
@GetMapping("/list") |
|
|
@GetMapping("/list") |
|
|
public TableDataInfo list(ZhyPoint zhyPoint) |
|
|
public TableDataInfo list(ZhyPoint zhyPoint) |
|
|
{ |
|
|
{ |
|
|
|