Original Code

  • I had originally written some code that didn't save the data in volumes
    • Volumes is a persistent folder meaning items stored there are saved after the project is shut down
    • Not storing in Volumes can result in the files getting deleted --> won't be there next time user logs in
  • Let's dive into Mr. M's code since mine is just a simplified version of his

UploadController.java

  • Two major parts to the controller...
    • Uploader
      • Responsible for collecting the file, converting it into bytes to be stored, and storing required metadata in database (this feature currently not functioning)
    • Upload
      • Responsible for taking the uploaded files and displaying them to the user
      • I had initially thought the code would loop through volumes and compile the images in a list but it appears it only lists the files the user just uploaded? Or maybe that's because I am working on localhost instead of a deployed site? Or maybe it's cuz the database is not working. Still need to figure these questions out

Uploader Code...

// uploader page: filesystem and database management of uploaded image
    @PostMapping("/mvc/uploader")
    public String mvcUploader(@RequestParam("filename") MultipartFile formFile, Model modelMap) {
        /*
         * The static directory is loaded at startup. UploadING images or makING changes
         * to any files or
         * folders under the static folder will not reflect as ApplicationContext is
         * already initialized.
         */
        String filePath = "volumes/uploads/"; // thus, uploads defined outside of static for dynamic updates
        String webPath = "/" + filePath; // webPath

        // A database table, using Upload POJO, is remembers location of upload and
        // associated metadata
        Upload repoFile = new Upload();
        repoFile.setFile(webPath + formFile.getOriginalFilename());
        repoFile.setType(formFile.getContentType());
        repoFile.setSize(formFile.getSize());

        // try/catch is in place, but error handling is not implemented (returns without
        // alerts)
        try {
            // Creating the directory to store file
            File dir = new File(filePath);
            if (!dir.exists())
                dir.mkdirs();

            // Create the file on server
            byte[] bytes = formFile.getBytes();

            // File write alternatives (going with Stream for now as in theory it would be
            // non-blocking)
            String path = filePath + formFile.getOriginalFilename();
            File serverFile = new File(path);
            BufferedOutputStream stream = new BufferedOutputStream(
                    new FileOutputStream(serverFile));
            stream.write(bytes);
            stream.close();

            // JPA save
            repo.save(repoFile);

        } catch (IOException e) {
            e.printStackTrace(); // app stays alive, errors go to run console, /var/log/syslog
        }

        // Redirect back to action page
        return "redirect:/mvc/upload";

    }

Break down...

  • URL for calling the function is /mvc/uploader
  • Takes the multipart file (like an image for example) that the user inputs
  • Directs all files to a directory in volumes titled uploads
  • The following creates the uploads directory if it didn't already exist. If it does exist, the code will continue without executing this section
File dir = new File(filePath);
if (!dir.exists())
    dir.mkdirs();
  • The following code is supposed to add metadata of the uploaded file to a database table...but it wasn't doing that...need to figure out why
Upload repoFile = new Upload();
repoFile.setFile(webPath + formFile.getOriginalFilename());
repoFile.setType(formFile.getContentType());
repoFile.setSize(formFile.getSize());

The code following that takes the file and converts it to bytes since that's how the code can read and store the file.

byte[] bytes = formFile.getBytes();
  • Then the code puts the file in volumes/uploads and saves it for the next user login
  • Utilizes output string to do this --> take bytes and stores them in a location
String path = filePath + formFile.getOriginalFilename();
File serverFile = new File(path);
BufferedOutputStream stream = new BufferedOutputStream(
        new FileOutputStream(serverFile));
stream.write(bytes);
stream.close();

// JPA save
repo.save(repoFile);

Upload Code...

// user action page: upload controls and displays a history of images
    @GetMapping("/mvc/upload")
    public String mvcUpload(Model model) {
        List<Upload> files = repo.findAll(); // extract image history
        System.out.println("Number of files uploaded: " + files.size());
        for (int i = 0; i < files.size(); i++) {
            System.out.println(files.get(i).getFile());
        }
        model.addAttribute("files", files);

        return "mvc/upload";
    }

Breakdown...

  • This part of the controller stores in a list all the values associated with the file based on upload class in upload.java
  • List files is a list of the files the user uploaded</li>
  • The print line and for loop was just for me to test...in the terminal it prints how many files are saved in the upload list --> realized that this only takes into account recently uploaded files
  • Code ends with running the html file stored in resources/mvc
    • Originally tried to create upload.html in frontend...although I could call the frontend html file, I got a cross origins error (iirc) and decided to just have the file be in backend
    • OMG I JUST REALIZED maybe it's cuz Github pages doesn't support post method? So either way it wouldn't have worked out I think
  • </ul>

    Upload.html

    • (Commented out code is just me testing and trying to display the image lol)
    </div> </div> </div>
    <!DOCTYPE html>
    <html>
        <body><p id="para-test">Uploaded Images: </p>
        <form enctype="multipart/form-data" method="POST">
        <!--    <image th:src="@{__${files.file}__}" width="150px"></image>
        <image src="/volumes/uploads/ahemKamisatoKids.png"></image>-->
        <div id="image-list" th:each="file : ${files}">
            <h4 th:text="${file.file}" /> 
            <image th:src="${file.file}" width="150px"></image>
            <!-- Other Properties -->
        </div>
        </form>
        </body>
    </html>
    
    <body><p id="para-test">Uploaded Images: </p>
        <form enctype="multipart/form-data" method="POST">
    
    • Since uploaded item is an image...enctype is multipart/form-data --> images are multipart files
    <div id="image-list" th:each="file : ${files}">
            <h4 th:text="${file.file}" /> 
            <image th:src="${file.file}" width="150px"></image>
    
    • The above code snippet takes the image-list from the mvcUpload class in controller and displays it along with the file name --> name taken from the stored metadata

    MvcConfig.java

    • This is where the resource handlers are stored. Needed to serve static resources (things that aren't server generated but need to be sent to the browser once they are requested)
    • Used resourceHandler and resourceLocations...Handler is handling the static files while Locations is assigning a location to permanently store them --> needed to make volumes persistent
    @Override
        public void addResourceHandlers(final ResourceHandlerRegistry registry) {
            registry.addResourceHandler("/volumes/uploads/**").addResourceLocations("file:volumes/uploads/");
        }
    

    Upload2.html

    • Frontend code...
    <script>
    function uploadFile()
                {
                   alert("Hello Prisha"+ fileInput.files[0].name);
                   var innerhtml = document.getElementById("img-test").innerHTML;
                   console.log("testing");
                   var formdata = new FormData();
                   formdata.append("filename", fileInput.files[0]);
                   var requestOptions = {
                   method: 'POST',
                   body: formdata,
                   redirect: 'follow'
                   };
                   console.log("before calling the URL");
                   fetch("http://localhost:8192/mvc/uploader", requestOptions)
                   .then(response => response.text())
                   .then(result => console.log(result))
                   .catch(error => console.log('error', error));
                   alert("Completed");
                }           
    </script>
    
    • Fetch request to backend...alerts and consoles intertwined for testing (initially the code ran but no result was seen, needed to include alerts for debugging)
    <html>
        <body>
            <form id='formid'> 
                <input type="file" name="fileInput" id="fileInput">
                <button onclick="uploadFile()" name="submit">Submit</button>
            </form> 
            <br>
            <iframe id="img-test" src="http://localhost:8192/mvc/upload" width="100%" height="100%"> </iframe>
        </body>
    </html>
    
    • Html button to call code. The iframe feature is used to display the mvc/upload.html in backend.
    </div>