-1

I am trying to return a list of files from a directory. Here's my code:

package com.demo.web.api.file;

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.demo.core.Logger;

import io.swagger.v3.oas.annotations.Operation;

@RestController
@RequestMapping(value = "/files")
public class FileService {

    private static final Logger logger = Logger.factory(FileService.class);

    @Value("${file-upload-path}")
    public String DIRECTORY;

    @Value("${file-upload-check-subfolders}")
    public boolean CHECK_SUBFOLDERS;

    @GetMapping(value = "/list")
    @Operation(summary = "Get list of Uploaded files")
    public ResponseEntity<List<File>> list() {

        List<File> files = new ArrayList<>();

        if (CHECK_SUBFOLDERS) {

            // Recursive check
            try (Stream<Path> walk = Files.walk(Paths.get(DIRECTORY))) {
                List<Path> result = walk.filter(Files::isRegularFile).collect(Collectors.toList());

                for (Path p : result) {
                    files.add(p.toFile().getAbsoluteFile());
                }
            } catch (Exception e) {

                logger.error(e.getMessage());

            }

        } else {

            // Checks the root directory only.
            try (Stream<Path> walk = Files.walk(Paths.get(DIRECTORY), 1)) {
                List<Path> result = walk.filter(Files::isRegularFile).collect(Collectors.toList());

                for (Path p : result) {
                    files.add(p.toFile().getAbsoluteFile());
                }
            } catch (Exception e) {

                logger.error(e.getMessage());

            }

        }

        return ResponseEntity.ok().body(files);

    }

}

As seen in the code, I am trying to return a list of files.

However, when I test in PostMan, I get a list of string instead. enter image description here

How can I make it return the file object instead of the file path string? I need to get the file attributes (size, date, etc.) to display in my view.

ericute
  • 144
  • 1
  • 2
  • 15
  • 1
    You can not return the File object, because it is not serializeable. You have to create your own FileDTO and put all the information into it – Jens Oct 20 '22 at 15:30
  • BTW: Take care of java naming conventions. variable names should be lower case and camelCase – Jens Oct 20 '22 at 15:30
  • I would tend to [this direction](https://stackoverflow.com/q/44342009/592355) ("multipart response") ..but you can also deliver it as JSON (with byte[]s) – xerx593 Oct 20 '22 at 16:19
  • I needed to rethink this. I don't need to get the actual files, just the size of the file for display. I'll post the solution I came up with. – ericute Oct 21 '22 at 05:51

3 Answers3

0

I would recommend that you change your ResponseEntity<> to return not a List of File but instead, a List of Resource, which you can then use to obtain the file metadata that you need.

public ResponseEntity<List<Resource>> list() {}

You can also try specifying a produces=MediaType... param in your @GetMapping annotation so as to tell the HTTP marshaller which kind of content to expect.

0

You'd have to Create a separate payload with the details you wanna respond with.

    public class FilePayload{
       private String id;
       private String name;
       private String size;

       public static fromFile(File file){
         // create FilePayload from File object here
        }

     }

And convert it using a mapper from your internal DTO objects to payload ones.

final List<FilePayload> payload = files.forEach(FilePayload::fromFile).collect(Collectors.toList());

return new ResponseEntity<>(payload, HttpStatus.OK);

I think you should not return a body in this case as you may be unaware of the size. Better to have another endpoint to GET /files/{id}

Abhi
  • 94
  • 7
0

I did give this another thought. What I just needed was the filename, size and date of the file. From there, I can get the file extension and make my list display look good already.

Here's the refactored method:

@GetMapping(value = "/list")
@Operation(summary = "Get list of Uploaded files")
public ResponseEntity<String> list() {

    JSONObject responseObj = new JSONObject();
    List<JSONObject> files = new ArrayList<>();

    // If CHECK_SUBFOLDERS is true, pass MAX_VALUE to make it recursive on all
    // sub-folders. Otherwise, pass 1 to use the root directory only.
    try (Stream<Path> walk = Files.walk(Paths.get(DIRECTORY), CHECK_SUBFOLDERS ? MAX_VALUE : 1)) {
        List<Path> result = walk.filter(Files::isRegularFile).collect(Collectors.toList());
        for (Path p : result) {
            JSONObject file = new JSONObject();
            file.put("name", p.toFile().getName());
            file.put("size", p.toFile().length());
            file.put("lastModified", p.toFile().lastModified());
            files.add(file);
        }
        responseObj.put("data", files);

    } catch (Exception e) {
        String errMsg = CoreUtils.formatString("%s: Error reading files from the directory: \"%s\"",
                e.getClass().getName(), DIRECTORY);
        logger.error(e, errMsg);
        responseObj.put("errors", errMsg);
    }

    return ResponseEntity.ok().body(responseObj.toString());

}

The above was what I ended up doing. I created a JSONObject of the props I need, and then returned the error if it did not succeed. This made it a lot better for me.

ericute
  • 144
  • 1
  • 2
  • 15