cft

Developing a File Upload Service From Scratch Using Java and Spring Boot

We will develop a file upload service, which will be used for image upload in our e-Commerce App


user

Nilmadhab

3 years ago | 6 min read



Photo by Drew Coffman on Unsplash

We needed an image upload functionality for our E-Commerce App, as we need to upload images for products and categories. We will build this function as a standalone service, which can be reused later. You can use this source code for uploading and displaying images for any of your products.

First, we will build the backend in Java and Spring, then integrate it with a Web client and Android Client in other tutorials

Backend Demo

You can test the file upload demo here

Find the complete code here

Backend Design

We will have 3 APIs

1. Upload image

2. Get an image by its name

3. Get all images

Let’s have a look at FileUploadController Java class which describe above 3 APIs

package com.webtutsplus.ecommerce.controller;

import com.webtutsplus.ecommerce.model.FileInfo;
import com.webtutsplus.ecommerce.service.FIleStoreService;
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.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;

import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@RestController
@RequestMapping("/fileUpload")
public class FileUploadController {

@Autowired
FIleStoreService fileStoreService;

//upload a file
@PostMapping("/")
public String handleFileUpload(@RequestParam("file") MultipartFile file) {
return fileStoreService.store(file);
}


// get all the files
@GetMapping("/")
public ResponseEntity<List<FileInfo>> getListFiles() {

// first get a stream of all file path present in root file directory
Stream<Path> pathStream = fileStoreService.loadAll();

List<FileInfo> fileInfos = pathStream.map(path -> {
// get file name
String filename = path.getFileName().toString();

// use function to get one file to build the URL
String url = MvcUriComponentsBuilder
.fromMethodName(FileUploadController.class, "getFile", path.getFileName().toString()).build().toString();
// make a fileinfo object from filename and url
return new FileInfo(filename, url);

}).collect(Collectors.toList());

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

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

}

which calls the FileStoreService which also contains 3 methods for each APIs respectively. I have added plenty of comments to the code. Please comment below if anything is not clear.

package com.webtutsplus.ecommerce.service;

import com.webtutsplus.ecommerce.constants.Constants;
import com.webtutsplus.ecommerce.exceptions.StorageException;
import org.apache.commons.io.FilenameUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

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

@Service
public class FIleStoreService {

Path rootLocation = Paths.get(Constants.UPLOAD_FILE_DIR);

public String store(MultipartFile file) {
try {
if (file.isEmpty()) {
throw new StorageException("Failed to store empty file.");
}
// find extension of the file,png or jpg
String extension = FilenameUtils.getExtension(file.getOriginalFilename());

// generate a random unique name for the image
String uploadedFileName = UUID.randomUUID().toString() + "." + extension;

// create a path for destination file
Path destinationFile = rootLocation.resolve(Paths.get(uploadedFileName))
.normalize().toAbsolutePath();

// Copy input file to destination file path
try (InputStream inputStream = file.getInputStream()) {
Files.copy(inputStream, destinationFile,
StandardCopyOption.REPLACE_EXISTING);

final String baseUrl =
ServletUriComponentsBuilder.fromCurrentContextPath().build().toUriString();

//create the public Image URl where we can find the image
final StringBuilder imageStringBuilder = new StringBuilder(baseUrl);
imageStringBuilder.append("/fileUpload/files/");
imageStringBuilder.append(uploadedFileName);

return imageStringBuilder.toString();
}
}
catch (IOException e) {
throw new StorageException("Failed to store file.", e);
}
}

public Stream<Path> loadAll() {
// load all the files
try {
return Files.walk(this.rootLocation, 1)
// ignore the root path
.filter(path -> !path.equals(this.rootLocation))
.map(this.rootLocation::relativize);
}
catch (IOException e) {
throw new StorageException("Failed to read stored files", e);
}

}

public Resource load(String filename) {
try {
// read the file based on the filename
Path file = rootLocation.resolve(filename);
// get resource from path
Resource resource = new UrlResource(file.toUri());

if (resource.exists() || resource.isReadable()) {
return resource;
} else {
throw new RuntimeException("Could not read the file!");
}
} catch (MalformedURLException e) {
throw new RuntimeException("Error: " + e.getMessage());
}
}
}

That’s it, files will be now renamed with a unique name saved into **_UPLOAD_FILE_DIR_** directory*.*

[

Upload a file

Get all the files

get all the uploaded files

Download a single file by name

Next step

We will build an Android Application, which will use the APIs. The final result will look something like this.

This story was originally published in my medium profile.

Follow me on Twitter to read my latest posts.

Originally published here

Upvote


user
Created by

Nilmadhab

Developer @Booking.com | ex: Samsung, OYO | IIT Kharagpur | Entrepreneur, founder of simplecoding.dev | connect me https://twitter.com/Nilmadhabmondal


people
Post

Upvote

Downvote

Comment

Bookmark

Share


Related Articles