Java 转换&;在Spring MVC中验证CSV文件上载
我有一个包含站点列表的客户实体,如下所示:Java 转换&;在Spring MVC中验证CSV文件上载,java,validation,spring-mvc,csv,Java,Validation,Spring Mvc,Csv,我有一个包含站点列表的客户实体,如下所示: public class Customer { @Id @GeneratedValue private int id; @NotNull private String name; @NotNull @AccountNumber private String accountNumber; @Valid @OneToMany(mappedBy="customer")
public class Customer {
@Id
@GeneratedValue
private int id;
@NotNull
private String name;
@NotNull
@AccountNumber
private String accountNumber;
@Valid
@OneToMany(mappedBy="customer")
private List<Site> sites
}
public class Site {
@Id
@GeneratedValue
private int id;
@NotNull
private String addressLine1;
private String addressLine2;
@NotNull
private String town;
@PostCode
private String postCode;
@ManyToOne
@JoinColumn(name="customer_id")
private Customer customer;
}
我的MVC配置将文件上载限制为1MB:
@Bean
public MultipartResolver multipartResolver() {
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
multipartResolver.setMaxUploadSize(1000000);
return multipartResolver;
}
是否有一种spring方式可以同时转换和验证,而不必打开CSV文件并循环两次,一次用于验证,另一次用于实际读取/填充数据?IMHO,将整个CSV加载到内存中是个坏主意,除非:
- 你肯定它总是非常小(如果用户点击了错误的文件怎么办?)
- 验证是全局的(只有真实的用例,但这里似乎没有)
- 您的应用程序永远不会在负载严重的生产环境中使用
MultipartFile
对象,或者使用一个包装器公开InputStream
(以及最终可能需要的其他信息)
然后仔细设计、编码和测试一个方法,将InputStream作为输入,逐行读取并调用逐行方法来验证和插入数据。差不多
class CsvLoader {
@Autowired Verifier verifier;
@Autowired Loader loader;
void verifAndLoad(InputStream csv) {
// loop through csv
if (verifier.verify(myObj)) {
loader.load(myObj);
}
else {
// log the problem eventually store the line for further analysis
}
csv.close();
}
}
这样,应用程序只使用它真正需要的内存,只在文件中循环一次
编辑:我所说的包装Spring MultipartFile的精确性
首先,我将验证分为两部分。正式验证在控制器层,仅控制以下各项:
- 有一个客户字段
- 文件大小和mimetype似乎正常(例如:size>12&&mimetype=text/csv)
SiteCSVFileValidator
只测试csv的mimetype和大小
通常,您避免直接从业务类使用Spring类。如果没有问题,控制器直接将MultipartFile发送到服务对象,同时传递BindingResult以直接填充最终的错误消息。控制器变为:
@RequestMapping(value="/new", method = RequestMethod.POST)
public String newCustomer(@Valid @ModelAttribute("customerForm") CustomerForm customerForm, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "NewCustomer"; // only external validation
} else {
/*
validation has passed, so now we must:
1) open customerForm.csvFile
2) loop through it to validate each line and populate customerForm.customer.sites
*/
customerService.insert(customerForm.customer, customerForm.csvFile, bindingResult);
if (bindingResult.hasErrors()) {
return "NewCustomer"; // only external validation
} else {
return "CustomerList";
}
}
}
在服务课上我们有
insert(Customer customer, MultipartFile csvFile, Errors errors) {
// loop through csvFile.getInputStream populating customer.sites and eventually adding Errors to errors
if (! errors.hasErrors) {
// actually insert through DAO
}
}
但是我们在服务层的方法中得到了两个Spring类。如果有问题,只需替换行customerService.insert(customerForm.customer、customerForm.csvFile、bindingResult)代码>带有:
List<Integer> linesInError = new ArrayList<Integer>();
customerService.insert(customerForm.customer, customerForm.csvFile.getInputStream(), linesInError);
if (! linesInError.isEmpty()) {
// populates bindingResult with convenient error messages
}
打电话
customerService.insert(customerForm.customer, new CsvFile(customerForm.csvFile), linesInError);
没有直接的Spring依赖性感谢您的反馈;我已经更新了我的原始问题,以显示我的MVC配置,该配置将文件上载限制为1MB(可能应该首先包括这个!)。CSV文件相对较小(平均5KB),因此双循环不应该引起问题,必须打开并读取文件两次似乎不是一个好的解决方案。你的CsvLoader回答让我感兴趣,但我不确定你所说的“使用包装器公开输入流”是什么意思-你能详细说明一下吗?(更多的示例代码将非常有用)。
insert(Customer customer, MultipartFile csvFile, Errors errors) {
// loop through csvFile.getInputStream populating customer.sites and eventually adding Errors to errors
if (! errors.hasErrors) {
// actually insert through DAO
}
}
List<Integer> linesInError = new ArrayList<Integer>();
customerService.insert(customerForm.customer, customerForm.csvFile.getInputStream(), linesInError);
if (! linesInError.isEmpty()) {
// populates bindingResult with convenient error messages
}
class CsvFile {
private String name;
private InputStream inputStream;
CsvFile(MultipartFile file) {
name = file.getOriginalFilename();
inputStream = file.getInputStream();
}
// public getters ...
}
customerService.insert(customerForm.customer, new CsvFile(customerForm.csvFile), linesInError);