loading...

Uploading a file - Svelte form and SpringBoot backend

brunooliveira profile image Bruno Oliveira ・4 min read

Introduction

Some web applications require files to be used for data entry or processing, and, typically, they come in the form of a user uploaded file that gets sent to the back-end for further processing.
The file is uploaded through a form and sent to the back-end where the file can be processed and a response sent back to the UI. There are many ways to go about it, but, here, I will build a very simple Svelte upload form for the front-end that uses the fetch API to upload the file and SpringBoot to maintain the endpoint that will receive and process the file.

The Svelte upload form

A very simple, barebones upload component will look like:

Alt Text

It will contain a basic file input html component and will show the name of the file when the user selects it.

The code for the component is the following:

<script>
    let files;
    let dataFile = null;


    function upload() {
        const formData = new FormData();
        formData.append('damName', value);
        formData.append('dataFile', files[0]);
        const upload = fetch('http://localhost:8080/file', {
            method: 'POST',
            body: formData
        }).then((response) => response.json()).then((result) => {
            console.log('Success:', result);
        })
                .catch((error) => {
                    console.error('Error:', error);
                });
    }
</script>

<input id="fileUpload" type="file" bind:files>

{#if dataFile && files[0]}
    <p>
        {files[0].name}
    </p>
{/if}

{#if value}
    <button on:click={upload}>Submit</button>
{:else}
    <button on:click={upload} disabled>Submit</button>
{/if}

In Svelte, a component is defined entirely in a single file, containing both styles and logic, embedded respectively, in the tags: <style></style> and <script></script>, just like in standard HTML files.

Below both of these tags (note, the styles are optional), you can define the standard HTML for the component, all bundled in a single file with the .svelte extension.

As we see, we have at our disposal a sort of templating engine that allows to define looping constructs, ifs, lifecycle constraints on the components, amongst many other things. Add to this the fact that all of it is bundled in a single source file, and it becomes very easy and intuitive to structure your Svelte applications in terms of components and data flow between them, and as I explore the framework more, I keep finding more interesting and powerful concepts, and I can at this point recommend everyone to try it!

Svelte feels to me like writing applications in the plain old CSS,JS and HTML stack, but, with exactly the right level of encapsulation and abstraction added on top, that offers great component building blocks for designing apps in a much cleaner way.

As per our component, we simply define a simple HTML file input field, and we bind the files variable to it. In Svelte, this means that when the value for that input field will change, it will be bound to the files variable.

On the submit button we add the logic to fill in and send the form to the backend using a POST request with the fetch API.

Note the let keyword to declare variables and also the if/else syntax, used to display the name of the file after the user uploaded it, and also to enable the submit button. This is a simple example of the Svelte template syntax.

With our front-end component written, we can now look at the backend code.

The SpringBoot back-end endpoint to receive a file

The fetch API shown above, will send a POST request to http://localhost:8080/file to send the file to the backend, that should be running. In our case, this will be a basic SpringBoot app, with a single endpoint listening on port 8080. Let's look at the code:

@PostMapping(value = "/file",consumes = "multipart/form-data", produces = "application/json")
    public ResponseEntity<List<PlotDataDTO>> uploadData(
        @RequestPart("dataFile") @Valid @NotNull @NotBlank MultipartFile dataFile) throws IOException {
        fileProcessingService.processDTO(new DamFileDTO(dataFile));
       return fileProcessingService.getPlotData();
    }

I will quickly highlight some useful things in this simple Spring endpoint that are important to get right:

  • this is an endpoint that only accepts POST requests, as indicated by the @PostMapping annotation on its header. This is because we are sending data;

  • the endpoint consumes data of type "multipart/form-data", to tell Spring that we are receiving data which originated from a form, as opposed to simple request body;

  • to keep in sync with the type of data that the endpoint consumes, in the method, we annotate our parameters as @RequestPart, to indicate that it's a part of the form we are receiving here. The name used here in the annotation needs to match the field in the form;

If we do this correctly, we will now have our uploaded file stored inside the dataFile variable. Then, we pass it to a FileProcessingService class, that can process it, encapsulating any logic within.

Our service can look like this, assuming we were reading a XLS file:

@Service
public class FileProcessingService {

    private final List<PlotDataDTO> dataToPlot = new ArrayList<>();

    public FileProcessingService() {
    }

    public void processDTO(DamFileDTO file) throws IOException {
        dataToPlot.clear();
        MultipartFile f = file.getDataFile();
        XSSFWorkbook workbook = new XSSFWorkbook(f.getInputStream());
        XSSFSheet worksheet = workbook.getSheetAt(0);
        for(int i=1;i<worksheet.getPhysicalNumberOfRows() ;i++) {
           PlotDataDTO plotData = new PlotDataDTO();
            XSSFRow row = worksheet.getRow(i);

            plotData.setTime(row.getCell(0).getNumericCellValue());
            plotData.setAcceleration(row.getCell(1).getNumericCellValue());
            dataToPlot.add(plotData);
        }
    }

So, we can extract and read the data in our uploaded file easily from our service and do any business logic there to prepare a response.

This completes the entire flow of uploading a file and reading it in the backend.

Conclusion

We saw how we can integrate Svelte and SpringBoot to upload and process a file and how flexible Svelte can be! Any questions, suggestions, tips, are welcome!

Discussion

markdown guide