当前位置: 首页 > news >正文

Excel 导入导出工具类文档

Excel 导入导出工具类详解:基于 Spring 和 Apache POI 的通用解决方案

本文分享一套功能强大的 Excel 导入导出工具类,基于 Spring 框架和 Apache POI 实现,支持注解驱动的字段映射、数据类型自动转换、样式定制等高级功能。

工具类核心功能

  • 智能导入:支持 Excel 数据到 Java 对象的自动转换
  • 灵活导出:Java 对象到 Excel 的自动导出
  • 注解驱动:通过注解控制字段映射行为
  • 类型转换:自动处理常见数据类型(日期、数值、布尔值等)
  • 错误处理:导入时跳过错误行,导出时标记错误字段

核心工具类源码

1. Excel 导入工具类 (ExcelImportUtil.java)

package com.xk.toolkit.util.file.excel;import com.xk.toolkit.util.file.excel.property.ExcelIgnore;
import com.xk.toolkit.util.file.excel.property.ExcelProperty;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.web.multipart.MultipartFile;import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;/*** Excel导入工具类** <p>提供通用的Excel导入功能,支持基于注解的字段映射和类型转换</p>** <p>功能特点:* <ul>*   <li>基于反射机制实现通用导入</li>*   <li>支持字段过滤(@ExcelIgnore)</li>*   <li>支持自定义列名和日期格式(@ExcelProperty)</li>*   <li>自动类型转换(字符串、数值、日期、布尔值)</li>*   <li>公式单元格处理</li>*   <li>智能列名匹配(支持大小写不敏感匹配)</li>*   <li>容错处理(跳过空行和错误行)</li>* </ul>*/
public class ExcelImportUtil {/*** 通用Excel导入方法** @param <T>   数据类型泛型* @param file  上传的Excel文件* @param clazz 目标对象类型* @return 导入成功的数据对象列表* @throws IOException 当文件解析失败时抛出** <p>执行流程:* <ol>*   <li>获取字段映射关系</li>*   <li>解析Excel文件</li>*   <li>构建列索引到字段的映射</li>*   <li>遍历数据行并转换为对象</li>*   <li>类型转换和字段赋值</li>*   <li>返回成功导入的对象列表</li>* </ol>*/public static <T> List<T> importExcel(MultipartFile file, Class<T> clazz) throws IOException {List<T> resultList = new ArrayList<>();// 获取字段映射(列名->字段对象)Map<String, Field> fieldMap = getFieldMap(clazz);// 使用try-with-resources确保流关闭try (InputStream inputStream = file.getInputStream();// 创建XSSFWorkbook解析.xlsx格式Workbook workbook = new XSSFWorkbook(inputStream)) {// 获取第一个工作表Sheet sheet = workbook.getSheetAt(0);// 获取表头行(第0行)Row headerRow = sheet.getRow(0);// 列索引到字段的映射Map<Integer, FieldMapping> columnMapping = new HashMap<>();// 1. 构建列映射关系 =======================================for (int col = 0; col < headerRow.getLastCellNum(); col++) {Cell cell = headerRow.getCell(col);if (cell != null) {// 获取列名并去除空格String columnName = cell.getStringCellValue().trim();// 根据列名查找对应字段Field field = findFieldByColumnName(fieldMap, columnName);if (field != null) {// 存储映射关系:列索引 -> (字段对象, 日期格式)columnMapping.put(col, new FieldMapping(field, getDateFormat(field)));}}}// 2. 处理数据行 ==========================================for (int rowNum = 1; rowNum <= sheet.getLastRowNum(); rowNum++) {Row row = sheet.getRow(rowNum);// 跳过空行if (row == null) continue;try {// 通过反射创建对象实例T obj = clazz.getDeclaredConstructor().newInstance();// 标记行是否有有效数据boolean hasData = false;// 遍历所有列for (int col = 0; col < headerRow.getLastCellNum(); col++) {Cell cell = row.getCell(col);// 跳过空单元格if (cell == null) continue;// 获取列映射信息FieldMapping mapping = columnMapping.get(col);if (mapping != null) {// 解析单元格值(根据目标类型和日期格式)Object value = parseCellValue(cell, mapping.field.getType(), mapping.dateFormat);if (value != null) {// 设置字段可访问(私有字段)mapping.field.setAccessible(true);// 给字段赋值mapping.field.set(obj, value);// 标记有有效数据hasData = true;}}}// 如果有有效数据则添加到结果列表if (hasData) {resultList.add(obj);}} catch (Exception e) {// 记录错误行信息(实际应用中应使用日志框架)System.err.printf("导入第 %d 行数据出错: %s%n", rowNum + 1, e.getMessage());}}} catch (Exception e) {throw new IOException("文件解析失败: " + e.getMessage(), e);}return resultList;}/*** 获取字段映射关系** @param clazz 目标类* @return 列名到字段对象的映射Map** <p>映射规则:* <ul>*   <li>忽略带@ExcelIgnore注解的字段</li>*   <li>优先使用@ExcelProperty的value作为键</li>*   <li>无注解时使用字段名作为键</li>* </ul>*/private static Map<String, Field> getFieldMap(Class<?> clazz) {Map<String, Field> fieldMap = new HashMap<>();// 遍历所有声明字段for (Field field : clazz.getDeclaredFields()) {// 跳过忽略字段if (field.isAnnotationPresent(ExcelIgnore.class)) continue;// 获取ExcelProperty注解ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);// 确定映射键:注解值优先,否则使用字段名String key = (annotation != null && !annotation.value().isEmpty()) ?annotation.value() : field.getName();fieldMap.put(key, field);}return fieldMap;}/*** 根据列名查找字段** @param fieldMap   字段映射表* @param columnName Excel列名* @return 匹配的字段对象,未找到返回null** <p>匹配策略:* <ol>*   <li>精确匹配(区分大小写)</li>*   <li>忽略大小写匹配</li>* </ol>*/private static Field findFieldByColumnName(Map<String, Field> fieldMap, String columnName) {// 1. 精确匹配if (fieldMap.containsKey(columnName)) {return fieldMap.get(columnName);}// 2. 忽略大小写匹配for (String key : fieldMap.keySet()) {if (key.equalsIgnoreCase(columnName)) {return fieldMap.get(key);}}return null;}/*** 获取字段的日期格式** @param field 目标字段* @return 日期格式字符串(默认"yyyy-MM-dd HH:mm:ss")*/private static String getDateFormat(Field field) {ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);// 优先使用注解指定的格式,否则使用默认格式return (annotation != null) ? annotation.dateFormat() : "yyyy-MM-dd HH:mm:ss";}/*** 解析单元格值** @param cell       单元格对象* @param targetType 目标Java类型* @param dateFormat 日期格式(仅对日期类型有效)* @return 转换后的Java对象** <p>处理逻辑:* <ul>*   <li>字符串类型 -> convertStringValue()</li>*   <li>数值类型 -> 判断是否为日期</li>*   <li>布尔类型 -> 直接返回</li>*   <li>公式类型 -> 解析公式结果</li>* </ul>*/private static Object parseCellValue(Cell cell, Class<?> targetType, String dateFormat) {// 根据单元格类型分发处理switch (cell.getCellType()) {case STRING:// 字符串类型处理return convertStringValue(cell.getStringCellValue().trim(), targetType, dateFormat);case NUMERIC:// 数值类型:判断是否为日期格式if (DateUtil.isCellDateFormatted(cell)) {// 日期类型处理return convertDateValue(cell.getDateCellValue(), targetType);} else {// 普通数值处理return convertNumericValue(cell.getNumericCellValue(), targetType);}case BOOLEAN:// 布尔类型直接返回return cell.getBooleanCellValue();case FORMULA:// 公式单元格特殊处理return parseFormulaCell(cell, targetType, dateFormat);default:// 其他类型返回nullreturn null;}}/*** 解析公式单元格** @param cell       公式单元格* @param targetType 目标Java类型* @param dateFormat 日期格式* @return 公式计算结果的转换值*/private static Object parseFormulaCell(Cell cell, Class<?> targetType, String dateFormat) {try {// 根据公式结果类型处理switch (cell.getCachedFormulaResultType()) {case STRING:return convertStringValue(cell.getStringCellValue().trim(), targetType, dateFormat);case NUMERIC:if (DateUtil.isCellDateFormatted(cell)) {return convertDateValue(cell.getDateCellValue(), targetType);} else {return convertNumericValue(cell.getNumericCellValue(), targetType);}case BOOLEAN:return cell.getBooleanCellValue();default:return null;}} catch (Exception e) {// 公式解析异常返回nullreturn null;}}/*** 转换字符串值到目标类型** @param value      字符串值* @param targetType 目标Java类型* @param dateFormat 日期格式* @return 转换后的Java对象* @throws IllegalArgumentException 当转换失败时抛出** <p>支持转换类型:* <ul>*   <li>String</li>*   <li>Integer/int</li>*   <li>Long/long</li>*   <li>Double/double</li>*   <li>Boolean/boolean(支持"是/否"、"YES/NO"、"TRUE/FALSE"、"1/0")</li>*   <li>LocalDate(按指定格式)</li>*   <li>LocalDateTime(按指定格式)</li>* </ul>*/private static Object convertStringValue(String value, Class<?> targetType, String dateFormat) {if (value.isEmpty()) return null;try {// 字符串类型直接返回if (targetType == String.class) return value;// 整型转换if (targetType == Integer.class || targetType == int.class) return Integer.parseInt(value);// 长整型转换if (targetType == Long.class || targetType == long.class) return Long.parseLong(value);// 双精度转换if (targetType == Double.class || targetType == double.class) return Double.parseDouble(value);// 布尔类型智能转换if (targetType == Boolean.class || targetType == boolean.class) {return "是".equals(value) || "YES".equalsIgnoreCase(value) ||"TRUE".equalsIgnoreCase(value) || "1".equals(value);}// 日期转换if (targetType == LocalDate.class) {return LocalDate.parse(value, DateTimeFormatter.ofPattern(dateFormat));}// 日期时间转换if (targetType == LocalDateTime.class) {return LocalDateTime.parse(value, DateTimeFormatter.ofPattern(dateFormat));}} catch (Exception e) {throw new IllegalArgumentException("值转换失败: " + value + " -> " + targetType.getSimpleName());}// 未匹配类型返回原始字符串return value;}/*** 转换数值到目标类型** @param value      数值* @param targetType 目标Java类型* @return 转换后的Java对象*/private static Object convertNumericValue(double value, Class<?> targetType) {// 整型转换if (targetType == Integer.class || targetType == int.class) return (int) value;// 长整型转换if (targetType == Long.class || targetType == long.class) return (long) value;// 双精度转换if (targetType == Double.class || targetType == double.class) return value;// 单精度转换if (targetType == Float.class || targetType == float.class) return (float) value;// 布尔转换(>0为true)if (targetType == Boolean.class || targetType == boolean.class) return value > 0;// 默认返回原始值return value;}/*** 转换Date对象到目标类型** @param date       Java Date对象* @param targetType 目标Java类型* @return 转换后的时间对象*/private static Object convertDateValue(Date date, Class<?> targetType) {if (date == null) return null;// Date类型直接返回if (targetType == Date.class) return date;// 转换为LocalDateTimeif (targetType == LocalDateTime.class) {return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();}// 转换为LocalDateif (targetType == LocalDate.class) {return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();}// 默认返回Datereturn date;}/*** 字段映射辅助类** <p>封装字段和对应的日期格式信息</p>*/private static class FieldMapping {// 目标字段final Field field;// 日期格式(来自@ExcelProperty注解)final String dateFormat;FieldMapping(Field field, String dateFormat) {this.field = field;this.dateFormat = dateFormat;}}
}

2. Excel 导出工具类 (ExcelExportUtil.java)

package com.xk.toolkit.util.file.excel;import com.mnsn.framework.law.util.property.ExcelIgnore;
import com.mnsn.framework.law.util.property.ExcelProperty;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;import java.io.IOException;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.util.List;/*** Excel导出工具类** <p>提供通用的Excel导出功能,支持基于注解的字段映射和样式定制</p>** <p>功能特点:* <ul>*   <li>基于反射机制实现通用导出</li>*   <li>支持字段过滤(@ExcelIgnore)</li>*   <li>支持自定义列名(@ExcelProperty)</li>*   <li>自动设置表头和数据样式</li>*   <li>自动调整列宽适应内容</li>*   <li>支持常见数据类型(数值、字符串、布尔值)</li>*   <li>响应流直接输出,避免内存溢出</li>* </ul>*/
public class ExcelExportUtil {/*** 通用Excel导出方法** @param <T>        数据类型泛型* @param response   Http响应对象,用于输出Excel文件流* @param dataList   要导出的数据列表* @param fileName   导出文件名(不含扩展名)* @param sheetName  工作表名称* @param clazz      数据对象类型(用于反射获取字段信息)* @throws IOException 当文件导出失败时抛出** <p>执行流程:* <ol>*   <li>创建工作簿和工作表</li>*   <li>创建带样式的表头行</li>*   <li>遍历数据列表填充数据行</li>*   <li>自动调整列宽</li>*   <li>设置响应头并输出Excel文件流</li>* </ol>*/public static <T> void exportExcel(HttpServletResponse response,List<T> dataList,String fileName,String sheetName,Class<T> clazz) throws IOException {// 使用XSSFWorkbook创建.xlsx格式的Excel工作簿(支持大文件)try (Workbook workbook = new XSSFWorkbook()) {// 创建工作表Sheet sheet = workbook.createSheet(sheetName);// 获取所有声明的字段(包括私有字段)Field[] fields = clazz.getDeclaredFields();// 创建表头行(第0行)Row headerRow = sheet.createRow(0);// 创建表头样式CellStyle headerStyle = createHeaderStyle(workbook);// 列索引计数器int colNum = 0;// 遍历所有字段for (Field field : fields) {// 设置可访问私有字段field.setAccessible(true);// 检查是否忽略该字段if (!field.isAnnotationPresent(ExcelIgnore.class)) {// 获取列名(优先使用注解值,无注解则使用字段名)String headerName = getHeaderName(field);// 创建表头单元格Cell cell = headerRow.createCell(colNum++);// 设置表头文本cell.setCellValue(headerName);// 应用表头样式cell.setCellStyle(headerStyle);}}// 创建数据单元格样式CellStyle dataStyle = createDataStyle(workbook);// 行索引计数器(从第1行开始)int rowNum = 1;// 遍历数据列表for (T data : dataList) {// 创建数据行Row row = sheet.createRow(rowNum++);// 重置列索引colNum = 0;// 遍历所有字段for (Field field : fields) {field.setAccessible(true);// 检查是否忽略该字段if (!field.isAnnotationPresent(ExcelIgnore.class)) {Cell cell;try {// 通过反射获取字段值Object value = field.get(data);// 创建数据单元格cell = row.createCell(colNum++);// 设置单元格值(根据类型处理)setCellValue(cell, value);// 应用数据样式cell.setCellStyle(dataStyle);} catch (IllegalAccessException e) {// 字段访问异常处理cell = row.createCell(colNum++);cell.setCellValue("[ERROR]");}}}}// 自动调整列宽(根据内容)for (int i = 0; i < colNum; i++) {sheet.autoSizeColumn(i);}// 设置响应内容类型为Excelresponse.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");response.setCharacterEncoding("utf-8");// 设置下载文件名(UTF-8编码处理中文)response.setHeader("Content-Disposition", "attachment; filename=" +URLEncoder.encode(fileName + ".xlsx", "UTF-8"));// 将工作簿写入响应输出流workbook.write(response.getOutputStream());}}/*** 创建表头样式** @param workbook Excel工作簿对象* @return 配置好的表头单元格样式** <p>样式特点:* <ul>*   <li>深蓝色背景</li>*   <li>白色粗体文字</li>*   <li>居中对齐</li>* </ul>*/private static CellStyle createHeaderStyle(Workbook workbook) {// 创建单元格样式CellStyle style = workbook.createCellStyle();// 创建字体Font font = workbook.createFont();// 设置粗体font.setBold(true);// 设置白色字体font.setColor(IndexedColors.WHITE.getIndex());// 应用字体style.setFont(font);// 设置深蓝色背景style.setFillForegroundColor(IndexedColors.DARK_BLUE.getIndex());// 设置实心填充style.setFillPattern(FillPatternType.SOLID_FOREGROUND);// 设置水平居中style.setAlignment(HorizontalAlignment.CENTER);return style;}/*** 创建数据行样式** @param workbook Excel工作簿对象* @return 配置好的数据单元格样式** <p>样式特点:* <ul>*   <li>四周边框</li>*   <li>无特殊背景色</li>* </ul>*/private static CellStyle createDataStyle(Workbook workbook) {CellStyle style = workbook.createCellStyle();// 设置上边框(细线)style.setBorderTop(BorderStyle.THIN);// 设置下边框(细线)style.setBorderBottom(BorderStyle.THIN);// 设置左边框(细线)style.setBorderLeft(BorderStyle.THIN);// 设置右边框(细线)style.setBorderRight(BorderStyle.THIN);return style;}/*** 获取字段的列名** @param field 实体类字段* @return 列名字符串(优先返回注解值)*/private static String getHeaderName(Field field) {// 检查是否有ExcelProperty注解if (field.isAnnotationPresent(ExcelProperty.class)) {// 返回注解的value值return field.getAnnotation(ExcelProperty.class).value();}// 默认返回字段名return field.getName();}/*** 设置单元格值(根据Java类型处理)** @param cell  目标单元格* @param value 要设置的值** <p>处理规则:* <ul>*   <li>null值 -> 空字符串</li>*   <li>数值类型 -> 转换为Double</li>*   <li>布尔类型 -> 直接设置</li>*   <li>其他类型 -> toString()结果</li>* </ul>*/private static void setCellValue(Cell cell, Object value) {if (value == null) {cell.setCellValue("");  // 空值处理} else if (value instanceof Number) {// 数值类型转换为Doublecell.setCellValue(((Number) value).doubleValue());} else if (value instanceof Boolean) {// 布尔类型直接设置cell.setCellValue((Boolean) value);} else {// 其他类型使用字符串表示cell.setCellValue(value.toString());}}
}

3. 注解系统

Excel 属性注解 (ExcelProperty.java)
package com.xk.toolkit.util.file.excel.property;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;// 列映射注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ExcelProperty {// 列名String value() default "";// 日期格式(仅对时间类型有效)String dateFormat() default "yyyy-MM-dd HH:mm:ss";
}
Excel 忽略注解 (ExcelIgnore.java)
package com.xk.toolkit.util.file.excel.property;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;// 忽略字段注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ExcelIgnore {
}
Excel 列宽注解 (ExcelColumnWidth.java)
package com.xk.toolkit.util.file.excel.property;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;// ExcelColumnWidth.java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ExcelColumnWidth {int value() default 20; // 列宽(字符数)
}

使用示例

1. 定义实体类

public class Employee {@ExcelProperty("员工ID")private Long id;@ExcelProperty("员工姓名")private String name;@ExcelProperty(value = "入职日期", dateFormat = "yyyy年MM月dd日")private LocalDate hireDate;@ExcelProperty("薪资")private Double salary;@ExcelProperty("是否在职")private Boolean active;@ExcelIgnoreprivate String password;// 省略 getter/setter
}

2. 导入 Excel 数据

@PostMapping("/import")
public ResponseEntity<String> importEmployees(@RequestParam("file") MultipartFile file) {try {List<Employee> employees = ExcelImportUtil.importExcel(file, Employee.class);employeeService.saveAll(employees);return ResponseEntity.ok("成功导入 " + employees.size() + " 条数据");} catch (IOException e) {return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("导入失败: " + e.getMessage());}
}

3. 导出 Excel 数据

@GetMapping("/export")
public void exportEmployees(HttpServletResponse response) throws IOException {List<Employee> employees = employeeService.getAllEmployees();ExcelExportUtil.exportExcel(response,employees,"员工数据报表","员工信息",Employee.class);
}

技术要点解析

  1. 智能类型转换

    • 字符串自动转换为目标类型(数值、日期、布尔值)
    • 支持多种布尔值表示形式(是/否、YES/NO、TRUE/FALSE、1/0)
    • 日期格式自定义(通过注解)
  2. 容错处理

    • 导入时自动跳过空行
    • 错误行记录到控制台(生产环境应替换为日志框架)
    • 导出时错误字段标记为"[ERROR]"
  3. 性能优化

    • 使用 try-with-resources 确保资源关闭
    • 反射字段缓存提高性能
    • 流式输出避免内存溢出
  4. 扩展性

    • 支持自定义样式(修改 createHeaderStyle/createDataStyle 方法)
    • 支持自定义列宽(通过 ExcelColumnWidth 注解)
    • 支持多种数据类型扩展

依赖配置

<!-- Spring Boot Web -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>3.2.0</version>
</dependency><!-- Apache POI -->
<dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>5.2.5</version>
</dependency>
<dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>5.2.5</version>
</dependency><!-- Jakarta EE (兼容 Spring Boot 3.x) -->
<dependency><groupId>jakarta.servlet</groupId><artifactId>jakarta.servlet-api</artifactId><version>6.0.0</version><scope>provided</scope>
</dependency>

总结

本文分享的 Excel 导入导出工具类具有以下优势:

  1. 开箱即用:简单注解配置即可实现复杂导入导出功能
  2. 高度可定制:支持自定义列名、日期格式、列宽等
  3. 智能类型转换:自动处理常见数据类型转换
  4. 企业级健壮性:完善的错误处理和容错机制
  5. 性能优化:适合处理中小规模数据(<10万行)

在实际项目中,您可以根据业务需求进一步扩展功能,例如:

  • 添加数据校验逻辑
  • 支持多Sheet页导出
  • 添加导出进度提示
  • 集成数据字典转换

希望这套工具类能帮助您提升开发效率,欢迎在评论区交流使用心得!

http://www.dtcms.com/a/283019.html

相关文章:

  • ubuntu中拷贝docker容器中的文件到宿主机
  • 万字长文解析 OneCode3.0 AI创新设计
  • 微服务的编程测评系统2
  • 2025年量化交易平台对比:付费与开源方案深度分析
  • nerdctl - 兼容 Docker 语法 的 containerd 命令行界面
  • 17.图像金字塔采样(放大,缩小处理)拉普拉斯金字塔
  • 闰年的历史由来与C语言实现详解
  • 7.16 拓扑排序 | 欧拉回路 |链表排序 前缀和
  • Vue在线预览Excel和Docx格式文件
  • Redis学习其一
  • Python学习之路(十三)-常用函数的使用,及优化
  • Redis读写策略深度解析:高并发场景下的缓存兵法
  • python基础语法9,用os库实现系统操作并用sys库实现文件操作(简单易上手的python语法教学)
  • 猫眼娱乐IOS开发一面手撕算法
  • 嵌入式学习笔记--MCU阶段--DAY06DHT11练习
  • AR智能巡检:电力行业数字化转型的“加速器”
  • 基于Llama的RAG 3种模型配置方法
  • 51c自动驾驶~合集7
  • 基于C#开发solidworks图库中文件(SLDPRT,SLDASM,SLDDRW等)转换为HTML和PDF,提供批量和实时转换
  • AI产品经理面试宝典第28天:自动驾驶与智慧交通融合面试题与答法
  • 自动驾驶激光3D点云处理系统性阐述及Open3D库函数应用
  • MR 处于 WIP 状态的WIP是什么
  • 小模型的价值重估:从“缩水版DeepSeek”到AI系统的基础执行单元20250716
  • Linux 挂载新磁盘导致原文件被隐藏解决方案
  • 【代码】Matlab鸟瞰图函数
  • sqli-labs靶场通关笔记:第23关 注释符过滤
  • 叉车机器人如何实现托盘精准定位?这项核心技术的原理和应用是什么?
  • 静默的田野守护者:Deepoc具身智能如何让除草机器人读懂大地密语
  • Mybatis08-使用pageHelper
  • 本地 AI 问答机器人搭建项目(Ollama + Qwen-7B + LangChain + FastAPI)