Igor Pilya
Igor Pilya

Reputation: 41

Upload CSV file from React and Spring Boot

Maybe anybody knows how to solve the problem of uploading files to the back-end. Send file from Postman looks like But after upload from React, I have exception like this:

2020-11-09 18:17:38.829 DEBUG 10764 --- [nio-8081-exec-7] o.s.web.servlet.DispatcherServlet : POST "/api/employees/save-from-csv", parameters={masked} 2020-11-09 18:17:38.829 DEBUG 10764 --- [nio-8081-exec-7] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.bank.controller.EmployeeController#uploadFile(MultipartFile, Model) 2020-11-09 18:17:38.831 DEBUG 10764 --- [nio-8081-exec-7] .w.s.m.m.a.ServletInvocableHandlerMethod : Could not resolve parameter [0] in public org.springframework.http.ResponseEntity<com.bank.message.ResponseMessage> com.bank.controller.EmployeeController.uploadFile(org.springframework.web.multipart.MultipartFile,org.springframework.ui.Model) throws java.io.IOException: Required request part 'file' is not present 2020-11-09 18:17:38.831 DEBUG 10764 --- [nio-8081-exec-7] .m.m.a.ExceptionHandlerExceptionResolver : Using @ExceptionHandler com.bank.exceptions.FileUploadExceptionAdvice#handleException(Exception, WebRequest) 2020-11-09 18:17:38.832 DEBUG 10764 --- [nio-8081-exec-7] o.s.w.s.m.m.a.HttpEntityMethodProcessor : No match for [application/json, text/plain, /], supported: [] 2020-11-09 18:17:38.833 WARN 10764 --- [nio-8081-exec-7] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'file' is not present] 2020-11-09 18:17:38.837 DEBUG 10764 --- [nio-8081-exec-7] o.s.web.servlet.DispatcherServlet : Completed 400 BAD_REQUEST

This code for sending a file from React form :

class UploadFiles extends Component{

state = {
    file : ''
};

componentDidMount = () => {
    const {file} = this.props;
    this.setState({ file })
};

uploadFile = ({target : {files}}) => {
    console.log(files[0]);
    let data = new FormData();
    data.append('file', files);


    axios.post("/api/employees/save-from-csv", data)
        .then(res => {console.log(res)})
};

render(){
    return(
        <div className="container">
            <div className="row">
                <div className="col-md-6">
                    <div className="form-group files color">
                        <label>Upload Your File </label>
                        <input type="file" onChange={this.uploadFile}/>
                    </div>
                </div>
            </div>
        </div>
    )
}}

Service

public void csvToEmployees(@RequestParam("file") MultipartFile file, Model model) {
    // validate file
    if (file.isEmpty()) {
        model.addAttribute("message", "Please select a CSV file to upload.");
        model.addAttribute("status", false);
    } else {

        try (Reader reader = new BufferedReader(new InputStreamReader(file.getInputStream()))) {

            CsvToBean csvToBean = new CsvToBeanBuilder(reader)
                    .withType(Employee.class)
                    .withIgnoreLeadingWhiteSpace(true)
                    .build();

            List users = csvToBean.parse();
            employeeRepository.saveAll(users);

            model.addAttribute("users", users);
            model.addAttribute("status", true);

        } catch (Exception ex) {
            model.addAttribute("message", "An error occurred while processing the CSV file.");
            model.addAttribute("status", false);
        }
    }

}

Controller

 @PostMapping("/employees/save-from-csv")
public ResponseEntity<ResponseMessage> uploadFile(@RequestParam("file") MultipartFile file, Model model) throws IOException {
    ResponseEntity<ResponseMessage> result = null;
    boolean finished = false;
    String message = "";

    if (CSVHelper.hasCSVFormat(file)) {
        try {
            service.csvToEmployees(file, model);
            message = "Uploaded the file successfully: " + file.getOriginalFilename();
            result = ResponseEntity.status(HttpStatus.OK).body(new ResponseMessage(message));
            finished = true;
        } catch (Exception e) {
            message = "Could not upload the file: " + file.getOriginalFilename() + "!";
            message += e;
            result = ResponseEntity.status(HttpStatus.UNPROCESSABLE_ENTITY).body(new ResponseMessage(message));
            finished = true;
        }
    }
    if (!CSVHelper.hasCSVFormat(file)){

        message = "File is empty!";
        result = ResponseEntity.status(HttpStatus.NOT_FOUND).body(new ResponseMessage(message));
    }
    if (!finished) {
        message = "Please upload a csv file!";
        result = ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseMessage(message));
    }
    return result;
}

CSVHelper

public class CSVHelper {

    public static boolean hasCSVFormat(MultipartFile file) {

        String TYPE = "text/csv";
        return TYPE.equals(file.getContentType());
    }

    public static List<Employee> csvToEmployees(InputStream is) {
        try (BufferedReader fileReader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
             CSVParser csvParser = new CSVParser(fileReader,
                     CSVFormat.DEFAULT.withFirstRecordAsHeader().withIgnoreHeaderCase().withTrim());) {

            List<Employee> employees = new ArrayList<>();

            Iterable<CSVRecord> csvRecords = csvParser.getRecords();

            for (CSVRecord csvRecord : csvRecords) {
                Employee employee = new Employee(
                        Long.parseLong(csvRecord.get("Id")),
                        csvRecord.get("Name"),
                        Long.parseLong(csvRecord.get("DepartmentId")),
                        Double.parseDouble(csvRecord.get("Salary")),
                        csvRecord.get("City"),
                        csvRecord.get("Street"),
                        csvRecord.get("BankName"),
                        csvRecord.get("CardNumber")
                );

                employees.add(employee);
            }

            return employees;
        } catch (IOException e) {
            throw new RuntimeException("fail to parse CSV file: " + e.getMessage());
        }
    }

}

UnitTest

@Test
    public void saveEmployeesFromCSV() throws Exception {
        String url = "/api/employees/save-from-csv";


        String csvBuilder = "name,departmentId,salary,city,street,bankName,cardNumber\n" +
                "Maxim,1,3855,Madrid,Street,Bank York,NY98675432100\n";
        InputStream is = new ByteArrayInputStream(csvBuilder.getBytes());

        MockMultipartFile mockFile = new MockMultipartFile("file", "employees.csv", "text/csv", is);

        MockHttpServletResponse responseMessage = mvc.perform(MockMvcRequestBuilders.multipart(url)
                .file(mockFile)
                .param("file", "employees2.csv"))
                .andReturn()
                .getResponse();
        assertEquals(responseMessage.getStatus(), 200);
    }

Upvotes: 0

Views: 3876

Answers (2)

Влад
Влад

Reputation: 19

To post the FormData using axios, you need specify in header that you are sending FormData, for that content-type should be multipart/form-data.

Check this, how to use FormData with axios:

const config = {     
    headers: { 'content-type': 'multipart/form-data' }
}

uploadFile = ({target : {files}}) => {
    console.log(files[0]);
    let data = new FormData();
    data.append('file', files);


    axios.post("/api/employees/save-from-csv", data, config)
        .then(res => {console.log(res)})
};

You can also try to stringify your files, because formdata works with strings/blobs:

let data = new FormData();
data.append('file', JSON.stringify(files));

Upvotes: 1

Amir Schnell
Amir Schnell

Reputation: 651

Ok so This took me some time, although it's a very small mistake you made.

In your react code, you set the array of files, that is passed as file in your FormData.

The correct thing to do here is to only set one File of the array as content for the FormData:

uploadFile = ({target : {files}}) => {
    console.log(files[0]);
    let data = new FormData();
    data.append('file', files[0]); <- this here is where the error was


    axios.post("/api/employees/save-from-csv", data)
        .then(res => {console.log(res)})
};

Upvotes: 0

Related Questions