使用 MultipartFile 的上传功能实现个人相册

整体思路大致为先通过 MultipartFile 处理上传,获取到上传图片存储的地址,通过获取图片名称来作为展示图片时的描述信息。

话不多说,直接上手:

因为要在数据库中存储上传上来的图片存储地址和图片描述信息,因此先进行数据库表的设计。 主键id,url 用来存放图片的存储地址,introduction 用来介绍图片信息。

model 层:

@Entity
public class Photos {
    @Id
    @GeneratedValue
    private int id;

    @Column(name = "url", nullable = false)
    private String url;

    @Column(name = "introduction", nullable = false)
    private String introduction;
    //省去 get/set方法
}

repository 层:(整合了 Spring Data Jpa)

public interface PhotosRepository extends JpaRepository<Photos,Integer> {
    Page<Photos> findAll(Pageable pageable);
}

使用了 jpa 自带的查找全部并且分页的方法。

controller 层:

@Controller
public class PhotosController {
   @Autowired
   private PhotosService photosService;
   @Autowired
   private PhotosRepository photosRepository;

   @RequestMapping("/list")
   public String list(Model model, @RequestParam(value = "page",defaultValue = "0") Integer page, @RequestParam(value = "size",defaultValue = "4") Integer size){
       Sort sort = new Sort(Sort.Direction.DESC,"id");
       Pageable pageable = PageRequest.of(page,size,sort);
       Page<Photos> photosList = photosRepository.findAll(pageable);
       model.addAttribute("photos",photosList);
       return "index";
   }
}

分页并将 photos 的信息存放在 model 中,只要数据库中有值,那么进入 index 页面就可以直接通过模板语言 Thymeleaf 直接去除 photos 的 id,url,introduction 等信息,并在前端得以展示。

那么关键点就来了,那就是使用 MultipartFile 上传工具类上传图片,并将所上传的图片信息保存于数据库中,方便后端取用。

先来说一下 MultipartFile 的常用配置信息:

//是否⽀支持 multipart 上传文件
spring.servlet.multipart.enabled=true 
//是否支持文件写入磁盘
spring.servlet.multipart.file-size-threshold=0
//上传文件的临时目录
spring.servlet.multipart.location=
//最大支持文件大小
spring.servlet.multipart.max-file-size=1024Mb
//最大支持请求大小
spring.servlet.multipart.max-request-sizee=1024Mb
//是否支持 multipart 上传文件时懒加载
spring.servlet.multipart.resolve-lazily=false

最大支持文件大小 和 最大支持请求大小 最好设置大一些

启动类:

@SpringBootApplication
public class Demo3Application {

	public static void main(String[] args) {
		SpringApplication.run(Demo3Application.class, args);
	}
	
    @Bean
    public TomcatServletWebServerFactory tomcatEmbedded() {
        TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
        tomcat.addConnectorCustomizers((TomcatConnectorCustomizer) connector -> {
            if ((connector.getProtocolHandler() instanceof AbstractHttp11Protocol<?>)) {
                //-1 means unlimited
                ((AbstractHttp11Protocol<?>) connector.getProtocolHandler()).setMaxSwallowSize(-1);
            }
        });
        return tomcat;
    }
}

TomcatServletWebServerFactory() 的作用是为了解决上传文件大于 10M 然后出现重置连接的问题。

controller 层:

@Controller
public class UploadController {
    @Autowired
    private PhotosRepository photosRepository;

	// UPLOADED_FOLDER 是一个固定地址,为本项目 static 文件夹下 images 的路径,此地址用来存储上传的图片
    private static String UPLOADED_FOLDER = "E:\\xuexi\\springboot-demo\\demo3\\src\\main\\resources\\static\\images\\thumbs\\";
    
    @PostMapping("/upload")
    public String singleFileUpload(@RequestParam("file") MultipartFile file,
                                   RedirectAttributes redirectAttributes) {
        if (file.isEmpty()) {
            redirectAttributes.addFlashAttribute("message", "Please select a file to upload");
            return "redirect:uploadStatus";
        }
        try {
            byte[] bytes = file.getBytes();
            Path path = Paths.get(UPLOADED_FOLDER + file.getOriginalFilename());
            Files.write(path, bytes);

            redirectAttributes.addFlashAttribute("message",
                    "You successfully uploaded '" + file.getOriginalFilename() + "'");
            Photos photos = new Photos();
            photos.setIntroduction(file.getOriginalFilename());
            photos.setUrl("/images/thumbs/" + file.getOriginalFilename().replaceAll("\\\\","/"));
            photosRepository.save(photos);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "redirect:/uploadStatus";
    }
}

读取文件字节流,读取文件名与 UPLOADED_FOLDER 构造出文件的本地存储地址,然后写入。并展示写入成功信息到

uploadStatus.html 文件。在重定向到 uploadStatus.html 文件之前,通过 photosRepository.save() 方法将数据存入 photos 对象,并保存到数据库。

前端页面:

选择文件和上传显示控件:


<form method="POST" action="/upload" enctype="multipart/form-data">
    <input type="file" name="file" /><br/><br/>
    <input type="submit" value="上传显示" />
</form>

绑定了 /upload 方法。

图片展示和分页:

<section id="main">
    <section class="thumbnails">
        <div th:each="photo : ${photos}">
            <a>
            <img th:src="@{{src}(src = ${photo.url})}" alt="" />
            <h3 th:text="${photo.introduction}">Lorem ipsum dolor sit amet</h3>
            </a>
        </div>
	</section>
</section>


<div class="with:80%">
	<div th:include="page :: pager" th:remove="tag"></div>
</div>

最后来一个 gif 展示一下效果: 详情点击