项目场景:
新旧网站替换,需要对历史旧的信息数据进行迁移,新旧网站是不同的第三方厂商做的,迁移历史数据由我们集成商这边去做了,旧网站提供了旧的信息数据(excel),以及每条信息包括的附件等,我们这边负责处理新旧网站之间的映射关系,然后去调用新网站提供的上传文件接口和新增接口去导入数据。
处理思路:
- 通过swagger-ui上传excel文件,读取了excel表格数据;
pom.xml中添加依赖:
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>4.0.1</version>
</dependency>
读取Excel工具类 AnalysisOfExcelUtil
public class AnalysisOfExcelUtil {
private static final String XLS = "xls";
private static final String XLSX = "xlsx";
private static Workbook getWorkbook(InputStream inputStream, String fileType) throws IOException {
Workbook workbook = null;
if (fileType.equalsIgnoreCase(XLS)) {
workbook = new HSSFWorkbook(inputStream);
} else if (fileType.equalsIgnoreCase(XLSX)) {
workbook = new XSSFWorkbook(inputStream);
}
return workbook;
}
/**
* 读取Excel文件内容
*
* @param file 要读取的Excel文件所在路径
* @return 读取结果列表,读取失败时返回null
*/
public static List<Row> readExcel(MultipartFile file,int startAnalysis, String type) throws Exception{
Workbook workbook = null;
FileInputStream inputStream=null;
try {
inputStream= (FileInputStream) file.getInputStream();
String fileName = file.getOriginalFilename();
// 获取Excel后缀名
String fileType = fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length());
workbook = getWorkbook(inputStream, fileType);
// 读取excel中的数据
List<Row>resultDataList = parseExcel(workbook,startAnalysis, type);
return resultDataList;
} catch (Exception e) {
throw e;
} finally {
try {
if (null != workbook) {
workbook.close();
}
if (null != inputStream) {
inputStream.close();
}
} catch (Exception e) {
throw e;
}
}
}
/**
* 解析excel
* @param workbook,startAnalysis(从第几个开始解析)
* @param startAnalysis
* @return
* @throws Exception
*/
private static List<Row> parseExcel(Workbook workbook ,int startAnalysis, String type) throws Exception {
List<Row> rowList = new ArrayList<Row>();
// 解析sheet
for (int sheetNum = 0; sheetNum < workbook.getNumberOfSheets(); sheetNum++) {
if (sheetNum<startAnalysis){
continue;
}
Sheet sheet = workbook.getSheetAt(sheetNum);
String sheetName = sheet.getSheetName();
// 校验sheet是否合法
if (sheet == null) {
continue;
}
// 获取第一行数据
int firstRowNum = sheet.getFirstRowNum();
Row firstRow = sheet.getRow(firstRowNum);
if (null == firstRow) {
}
// 解析每一行的数据,构造数据对象
int rowStart = firstRowNum + 1;
int rowEnd = sheet.getPhysicalNumberOfRows();
for (int rowNum = rowStart; rowNum < rowEnd; rowNum++) {
Row row = sheet.getRow(rowNum);
if (null == row) {
continue;
}
if(type == "roleImport"){
row.createCell(5).setCellValue(sheetName);
}
rowList.add(row);
}
}
return rowList;
}
}
调用工具类获取各个字段的值
List<Row> rows = AnalysisOfExcelUtil.readExcel(file, 0, "");
int len = rows.size();
Row item = rows.get(i);
//数字代表excel文件中的第几列
param1 = item.getCell(1) == null ? "" : item.getCell(1).toString();
param2 = item.getCell(2) == null ? "" : item.getCell(2).toString();
param3 = item.getCell(3) == null ? "" : item.getCell(3).toString();
- 处理新旧网站各个字段之间的映射关系;
处理映射关系时只需要将新旧网站的各个字段一一对应上即可,这里不过多赘述。 - 使用restTemplate分别调用信息新增和附件上传的接口。
这里为什么分别调用两个接口呢,其实就是新网站那边没有提供专门的迁移数据的接口,以最小改动为原则,所以只能使用现有存在的接口。言归正传,处理完数据,然后就该开始传输数据了。
MultiValueMap<String, Object> multiValueMap = new LinkedMultiValueMap<>();
String url= "http://ip:port/***/***";
multiValueMap.add("param1", value1);
multiValueMap.add("param2", value2);
multiValueMap.add("param3", value3);
//调用接口
HttpHeaders httpHeaders = new HttpHeaders();
headersAttachment.setContentType(MediaType.MULTIPART_FORM_DATA);
headersAttachment.setContentLength(fileSystemResource.getFile().length());
RestTemplate restTemplateContent = new RestTemplate();
HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(multiValueMap, httpHeaders);
responseEntityUpload = restTemplateContent.postForEntity(url, httpEntity, HashMap.class);
上述代码中我们可以看到,在将参数封装到multiValueMap之后,又进行了一次封装。这里也是重点所在:
如果只是普通的系统间进行接口调用,基本上都是使用json格式的,这里可以不用再进行封装,基本上restTemplate实例化之后的请求类型是适用的,或者在实例化时设置请求的类型为json格式后都是可以正常调用的。例如:
- 直接实例化使用
@Configuration
public class RestTemplateConfig {
@Bean
RestTemplate restTemplate(){
return new RestTemplate();
}
}
- 设置请求类型后再实例化使用
public RestTemplate restTemplate(RestTemplateBuilder builder) {
RestTemplate restTemplate = builder.build();// 生成一个RestTemplate实例
List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setSupportedMediaTypes(Collections.singletonList(MediaType.TEXT_PLAIN));
converter.setSupportedMediaTypes(Collections.singletonList(MediaType.APPLICATION_JSON_UTF8));
converter.setSupportedMediaTypes(Collections.singletonList(MediaType.TEXT_HTML));
messageConverters.add(converter);
restTemplate.setMessageConverters(messageConverters);
return restTemplate;
}
回到这里,因为网站前端接口是以表单的形式去向后端提交请求的,所以这里我们需要构造一下请求头,去模拟前端发送表单请求,我这里使用 postman 去测试了一下接口,然后拿到了请求头的信息,之后我们像上面写的那样设置请求头中的Content-type 等的信息啦!
问题与总结:
使用RestTemplate调用接口基本上按照规范去使用是不会出现太多问题的,我这里在使用中遇到的问题简单罗列一下,供大家参考:
- 发送请求时报错提示"no suitable HttpMessageConverter found for response type…"
异常信息说的是:没给content-type是[application/javascript;charset=utf-8]的response找到合适的HttpMessageConverter。所以呢
1、大家可以按照测试调用的接口,获取到请求头里面content-type,然后在RestTemplate添加对应的类型即可,上面也有详细说明。
2、当你仔细设置content-type等值后,启动项目 ,发现还是会报这个错,那你可能就要考虑参数是否正确的问题了,我当时遇到是这种情况,因为第三方接口给的参数里面其他字段都是按照驼峰命名法去命名的,只有一个字段不是这样命名的,我又想当然的都以驼峰命名法去传值,在调用接口的时候它不会很直白的说是某个参数值不对导致报这个错,一直以为是给的content-type类型的问题,所以就在这块耽搁了好长时间,我裂开。所以还是希望能够规范命名啊!!!