乐优商城(八)——上传logo
文章所用资源:fdfs包
创建目录如下:
分析
路径:/upload/image;
参数:file文件
请求方式:POST
三层架构
Controller
package com.leyou;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* @Description: 上传文件
* @author: 废柴少爷
* @date: 2021年02月06日 21:24
*/
@EnableDiscoveryClient
@SpringBootApplication
public class LyUploadApplication {
public static void main(String[] args) {
SpringApplication.run(LyUploadApplication.class);
}
}
package com.leyou.upload.web;
import com.leyou.upload.service.UploadService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
/**
* @Description: 上传文件控制层
* @author: 废柴少爷
* @date: 2021年02月06日 21:46
*/
@RestController
@RequestMapping("upload")
public class UploadController {
@Autowired
private UploadService uploadService;
@PostMapping("image")
public ResponseEntity<String> uploadImage(@RequestParam("file")MultipartFile file){
return ResponseEntity.ok(uploadService.uploadImage(file));
}
}
Service
在上传图片文件时,我们要考虑到我们接收的是不是图片类型(后缀名),内容是不是图片(病毒文件伪装成图片)等等,我们这里简单模拟一下这些问题的处理。
package com.leyou.upload.service;
import org.springframework.web.multipart.MultipartFile;
public interface UploadService {
String uploadImage(MultipartFile file);
}
package com.leyou.upload.service.impl;
import com.leyou.common.enums.ExceptionEnum;
import com.leyou.common.exception.LyException;
import com.leyou.upload.service.UploadService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
/**
* @Description: 上传文件业务层
* @author: 废柴少爷
* @date: 2021年02月06日 21:49
*/
@Service
@Slf4j
public class UploadServiceImpl implements UploadService {
private static final List<String> ALLOW_TYPES = Arrays.asList("image/jpeg","image/png","image/bmp");
@Override
public String uploadImage(MultipartFile file) {
try {
//校验文件类型
String contentType = file.getContentType();
if (!ALLOW_TYPES.contains(contentType)){
throw new LyException(ExceptionEnum.INVALID_FILE_TYPE);
}
//校验文件内容
BufferedImage image = ImageIO.read(file.getInputStream());
if (image == null){
throw new LyException(ExceptionEnum.INVALID_FILE_TYPE);
}
//目标路径
File dest = new File("F:/upload/",file.getOriginalFilename());
//保存到本地
file.transferTo(dest);
return "http://image.leyou.com/"+file.getOriginalFilename();
} catch (IOException e) {
//上传失败
log.error("上传失败!",e);
throw new LyException(ExceptionEnum.BRAND_SAVE_ERROR);
}
}
}
新增了两种错误类型,一种是内部错误导致文件上传失败(500),还有一种是参数错误导致文件无法上传(400)
UPLOAD_FILE_ERROR(500,"上传文件失败"),
INVALID_FILE_TYPE(400,"无效的文件类型"),
Resources
和其他服务一样上传服务要注册到eureka,编写自己name和port端口号,这里一般要设置下文件上传大小,避免上传过大的文件
server:
port: 8082
spring:
application:
name: upload-service
servlet:
multipart:
max-file-size: 5MB # 限制文件上传的大小
# Eureka
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
instance:
ip-address: 127.0.0.1
prefer-ip-address: true
Zuul
添加路由前缀
upload-service:
path: /upload/**
serviceId: upload-service
strip-prefix: false
优化
图片上传是文件的传输,如果也经过Zuul网关的代理,文件就会经过多次网路传输,造成不必要的网络负担。在高并发时,可能导致网络阻塞,Zuul网关不可用。这样我们的整个系统就瘫痪了。简单来说就是让网关专心做路由转发就好了,如果做文件传输,特别是多文件、大文件可能会很耗时,影响系统的执行效率,甚至导致系统瘫痪,所以上传文件要绕过zuul的缓存,不是不经过zuul,而是不对请求进行处理,直接转发,这样就不会有缓存也减少了阻塞的发生。
解决方法
对上传路径进行重写,出于仅暴露网关的前提,不能直接改页面路径,我们使用nginx对路径进行重写。打开nginx的安装目录中的nginx.conf文件,在server_name api.leyou.com下修改如下:
location /api/upload {
rewrite "^/(.*)$" /zuul/$1;
}
这里的意思是说监听api/upload路径,将其路径修改为zuul/api/upload。
记得nginx -s reload重新加载nginx,让配置生效。
FDFS
先思考一下,之前上传的功能,有没有什么问题?
上传本身没有任何问题,问题出在保存文件的方式,我们是保存在服务器机器,就会有下面的问题:
- 单机器存储,存储能力有限
- 无法进行水平扩展,因为多台机器的文件无法共享,会出现访问不到的情况
- 数据没有备份,有单点故障风险
- 并发能力差
这个时候,最好使用分布式文件存储来代替本地文件存储
FastDFS是由淘宝的余庆先生所开发的一个轻量级、高性能的开源分布式文件系统。用纯C语言开发,功能丰富:
- 文件存储
- 文件同步
- 文件访问(上传、下载)
- 存取负载均衡
- 在线扩容
适合有大容量存储需求的应用或系统。同类的分布式文件系统有谷歌的GFS、HDFS(Hadoop)、TFS(淘宝)等。
这部分可以去百度了解更多,这里我不多描述。
ubuntu安装fdfs
unzip libfastcommon-master.zip
./make.sh && ./make.sh install
/leyou/fdfs/tracker
/leyou/fdfs/storage
/tmp
测试