【Java学習】Spring Boot+Vuetifyでファイルのアップロードとダウンロードを作ってみた。-バックエンド側実装編-

プログラミング
スポンサーリンク

今回は、バックエンド側の実装編です。環境構築については、こちらからご覧ください。

スポンサーリンク
スポンサーリンク
スポンサーリンク

1.パッケージとクラスの作成

作成したFileUploadDownloadプロジェクトに画像のようにパッケージとクラスを作成します。ファイル名やパッケージの分け方は任意です。

2.クラスの内容

FilesController クラス

アップロードとダウンロードを処理しているクラス。このクラスの処理の理解が重要。

package com.nexflame.fupdl.controller;

import java.util.List;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;

import com.nexflame.fupdl.message.ResponseMessage;
import com.nexflame.fupdl.model.FileInfo;
import com.nexflame.fupdl.service.FilesStorageService;

@Controller
@CrossOrigin(origins = "*", maxAge = 3600)
public class FilesController {

  @Autowired
  FilesStorageService storageService;

  @PostMapping("/upload")
  public ResponseEntity<ResponseMessage> uploadFile(@RequestParam("file") MultipartFile file) {
    String message = "";
    try {
      storageService.save(file);

      message = "ファイルのアップロードに成功しました。: " + file.getOriginalFilename();
      return ResponseEntity.status(HttpStatus.OK).body(new ResponseMessage(message));
    } catch (Exception e) {
      message = "ファイルをアップロードできませんでした。: " + file.getOriginalFilename() + "!";
      return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).body(new ResponseMessage(message));
    }
  }

  @GetMapping("/files")
  public ResponseEntity<List<FileInfo>> getListFiles() {
    List<FileInfo> fileInfos = storageService.loadAll().map(path -> {
      String filename = path.getFileName().toString();
      String url = MvcUriComponentsBuilder
          .fromMethodName(FilesController.class, "getFile", path.getFileName().toString()).build().toString();

      return new FileInfo(filename, url);
    }).collect(Collectors.toList());

    return ResponseEntity.status(HttpStatus.OK).body(fileInfos);
  }

  @GetMapping("/files/{filename:.+}")
  @ResponseBody
  public ResponseEntity<Resource> getFile(@PathVariable String filename) {
    Resource file = storageService.load(filename);
    return ResponseEntity.ok()
        .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getFilename() + "\"").body(file);
  }
}

TestController クラス

VueとSpringがうまく連携できているかを確認するために作った処理。今回のアップロード、ダウンロードとは直接関係がないので、これは作らなくても良い。

package com.nexflame.fupdl.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/test")
public class TestController {

    /**
     * テスト表示画面
     * @return タイトルと内容のMap
     */
    @RequestMapping(value = "/entries/latest", method = RequestMethod.GET)
    public Entry getLatestEntry() {
        Entry entryData = new Entry();
        // TODO DBからデータを取得
        entryData.setTitle("VueRouterを使ってみた");
        entryData.setContent("VueRouterで簡単にページルーティングができました。なんとなくイメージはつかめた。");
        return entryData;
    }

    /**
     * データのクラス
     */
    private class Entry{
        private String title;
        private String content;

        public String getTitle() {
            return title;
        }

        public void setTitle(String title) {
            this.title = title;
        }

        public String getContent() {
            return content;
        }

        public void setContent(String content) {
            this.content = content;
        }
    }
}

ResponseMessage クラス

処理のメッセージを保持しておくクラス

package com.nexflame.fupdl.message;

public class ResponseMessage {
  private String message;

  public ResponseMessage(String message) {
    this.message = message;
  }

  public String getMessage() {
    return message;
  }

  public void setMessage(String message) {
    this.message = message;
  }

}

FileInfo クラス

ファイルの情報を保持しておく入れ物の定義。

package com.nexflame.fupdl.model;

public class FileInfo {
  private String name;
  private String url;

  public FileInfo(String name, String url) {
    this.name = name;
    this.url = url;
  }

  public String getName() {
    return this.name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public String getUrl() {
    return this.url;
  }

  public void setUrl(String url) {
    this.url = url;
  }
}

FilesStorageService インターフェース

package com.nexflame.fupdl.service;

import java.nio.file.Path;
import java.util.stream.Stream;

import org.springframework.core.io.Resource;
import org.springframework.web.multipart.MultipartFile;

public interface FilesStorageService {
  public void init();

  public void save(MultipartFile file);

  public Resource load(String filename);

  public void deleteAll();

  public Stream<Path> loadAll();
}

FilesStorageServiceImpl クラス(継承)

package com.nexflame.fupdl.service;

import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Stream;

import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.util.FileSystemUtils;
import org.springframework.web.multipart.MultipartFile;

@Service
public class FilesStorageServiceImpl implements FilesStorageService {

  private final Path root = Paths.get("uploads");

  @Override
  public void init() {
    try {
      Files.createDirectory(root);
    } catch (IOException e) {
      throw new RuntimeException("アップロード用のフォルダを初期化できません。");
    }
  }

  @Override
  public void save(MultipartFile file) {
    try {
      Files.copy(file.getInputStream(), this.root.resolve(file.getOriginalFilename()));
    } catch (Exception e) {
      throw new RuntimeException("ファイルを保存できません。 Error: " + e.getMessage());
    }
  }

  @Override
  public Resource load(String filename) {
    try {
      Path file = root.resolve(filename);
      Resource resource = new UrlResource(file.toUri());

      if (resource.exists() || resource.isReadable()) {
        return resource;
      } else {
        throw new RuntimeException("ファイルを読み込めません。");
      }
    } catch (MalformedURLException e) {
      throw new RuntimeException("Error: " + e.getMessage());
    }
  }

  @Override
  public void deleteAll() {
    FileSystemUtils.deleteRecursively(root.toFile());
  }

  @Override
  public Stream<Path> loadAll() {
    try {
      return Files.walk(this.root, 1).filter(path -> !path.equals(this.root)).map(this.root::relativize);
    } catch (IOException e) {
      throw new RuntimeException("ファイルを読み込めません。");
    }
  }
}

StorageService インターフェース

package com.nexflame.fupdl.storage;

import java.nio.file.Path;
import java.util.stream.Stream;

import org.springframework.core.io.Resource;
import org.springframework.web.multipart.MultipartFile;

public interface StorageService {
	void init();
	void store(MultipartFile file);
	Stream<Path> loadAll();
	Path load(String filename);
	Resource loadAsResource(String filename);
	void deleteAll();
}

FileUploadDownloadApplication クラス

SpringBoot の自動生成で作成されるクラス。今回は何も修正していない。

package com.nexflame.fupdl;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class FileUploadDownloadApplication {

	public static void main(String[] args) {
		SpringApplication.run(FileUploadDownloadApplication.class, args);
	}

}

ServletInitializer クラス

プロジェクト生成時に自動生成されなかったので今回は手動で作成した。本来は自動生成される?

package com.nexflame.fupdl;

import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

public class ServletInitializer extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(FileUploadDownloadApplication.class);
    }

}

3.その他の設定

ファイルのアップロードとダウンロードのファイルサイズの上限を決めるために設定ファイルを修正します。とりあえず、1ファイル2MBまで。

application.properties の設定変更

spring.servlet.multipart.max-file-size=2MB
spring.servlet.multipart.max-request-size=10MB

アップロード用フォルダの作成

FilesStorageServiceImpl クラスでファイルを格納するのは、「uploads」フォルダになるのでプロジェクト直下に「uploads」フォルダを空で新規作成しておく。

ここまででサーバー側の処理は問題ないと思うので、あとは、フロント側でファイルの処理を記述すればアップロードとダウンロードができるはず。次回は、フロント側をVueとVuetifyを使って記述していこうと思う。

4.参考リンク

ほぼ下記のサイトのコードをそのまま使わせてもらいました。

コメント

タイトルとURLをコピーしました