一.跨域解决方案CORS
1.1什么是跨域:
当前浏览器的地址栏上的路径与浏览器内部Ajax 发出的请求不一致(域名或者端口号或者协议有任何一个不同即视为请求不一致),就是跨域.
如果跨域调用会出现如下错误:
No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin
‘http://localhost:9100’ is therefore not allowed access. The response had HTTP status code 400.
前后端分离一定会产生跨域问题,解决跨域问题可以采用CORS方案.
1.2什么是CORS
CORS是一个W3C标准,全称"跨域资源共享".CORS需要浏览器和服务器同时支持,目前,所有的浏览器都支持该功能,IE浏览器不能低于IE10.它允许浏览器向跨源服务器发出XMLHTTPRequest请求,从而克服了AJAX
只能同源使用的限制.整个CROS通信过程,都是浏览器自动完成,不需要用户参与,对于开发者来说,CORS通信与同源的AJAX没有差别,代码完全一样,浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉,因此,实现CORS通信的关键是服务器,只要实现了CORS接口,就可以跨源通信.
请求的过程如下:
首先浏览器向服务器发出跨域请求,然后服务器给浏览器允许跨域的响应,然后浏览器发出请求,服务器给出响应.
具体如何实现? springMVC的版本在4.2或者以上的,可以Controller类上使用注解@CrossOrign实现跨域.
二.分布式文件存储
FastDFS是一个开源的轻量级分布式文件系统,它对文件进行管理,功能包括:文件存储,文件同步,文件访问,等.
FastDFS为互联网量身定制,充分考虑了冗余备份,负载均衡,线性扩容等机制,并注重高可用,高性能等指标,使用FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传,下载等服务.
FastDFS架构包括Tracker server和Storage Server 客户端请求Tracker sercer 进行文件上传,下载,通过Tracker server调度最终由Storage server完成文件上传和下载.
Tracker server 进行文件上传,下载,通过Tracker server 调度最终由Storage server完成文件上传和下载.
Tracker server负责负载均衡和调度,通过Tracker server 在文件上传时可以根据一些策略找到Stroage server提供文件上传服务,可以将tracker称为追踪服务器或调度服务器,Storger server作用是文件存储,客户端上传的文件最终都存储在Storage server中 ,Storage server没有实现自己的文件系统而是利用操作的文件系统来管理文件.可以将Stroage称为存储服务器.
上传流程:
客户端上传文件后存储服务器将文件id返回给客户端,此文件id用于以后访问该文件的索引信息,文件索引信息包括:组名,虚拟磁盘路径,数据两级目录,文件名
组名:文件上传后所在的Storage组名称,在文件上传成功后,有storage服务器返回,需要客户端自行保存.
虚拟磁盘路径:storage配置的虚拟路径,与磁盘选项store_path*对应.如果配置了store_path0则是M00,如果配置了store_path1则是M01,以此类推.
数据两级目录:storage服务器在每个虚拟磁盘路径下创建的两级目录,用来存储数据文件.
文件名:与文件上传时不同,是由存储服务器根据特点信息生成,文件名包含:源存储服务器ip地址,文件创建时间戳,文件大小,随机数和文件拓展名等信息.
FastDFS搭建:
在Docker中搭建FastDFS:
首先拉取镜像:docker pull morunchang/fastdfs
运行tracker:docker run -d --name tracker --net=host morunchang/fastdfs sh tracker.sh
运行storage:
docker run -d --name storage --net=host -e TRACKER_IP=:22122 -e GROUP_NAME= morunchang/fastdfs sh storage.sh
使用的网络模式是:-net=host,< your tracker server address > 替换为你机器的ip即可
< group name>是组名 ,即stroage的组
如果想要增加新的stroage服务器,再次运行该命令,但是要注意更换新的组名
修改nginx的配置
进入storage的容器内部,修改nginx.conf配置文件
dockers exec -it storage /bin/bash
进入后
vi /data/nginx/conf/nginx.conf
添加以下内容:
location /group1/M00 {
proxy_next_upstream http_502 http_504 error timeout invalid_header;
proxy_cache http-cache;
proxy_cache_valid 200 304 12h;
proxy_cache_key is_args$args;
proxy_pass http://fdfs_group1;
expires 30d;
}
退出容器:exit
重启容器: docker restart storage
文件存储微服务:
首先构建工程:
引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>net.oschina.zcx7878</groupId>
<artifactId>fastdfs-client-java</artifactId>
<version>1.27.0.0</version>
</dependency>
在resource文件夹下创建fdfs_client.conf的配置文件
添加以下内容:
connect_timeout = 60
network_timeout = 60
charset = UTF-8
http.tracker_http_port = 8080
tracker_server = 192.168.200.128:22122
connect_timeout:连接超时时间,单位为秒。
network_timeout:通信超时时间,单位为秒。发送或接收数据时。假设在超时时间后还不能发送或接收数据,则本次网络通信失败
charset: 字符集
http.tracker_http_port :.tracker的http端口
tracker_server: tracker服务器IP和端口设置
创建application.yml配置文件:
添加以下内容:
spring:
servlet:
multipart:
max-file-size: 10MB #单个文件大小
max-request-size: 10MB #设置总上传文件大小
application:
name: file #微服务的应用名称
server:
port: 9007
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:6868/eureka
instance:
prefer-ip-address: true
feign:
hystrix:
enabled: true
max-file-size是单个文件大小,max-request-size是设置总上传的数据大小
创建启动类:
@SpringBootApplication
@EnableEurekaClient
public class FileApplication {
public static void main(String[] args) {
SpringApplication.run(FileApplication.class);
}
}
文件信息封装:
文件上传一般都有文件的名字、文件的内容、文件的扩展名、文件的md5值、文件的作者等相关属性,我们可以创建一个对象封装这些属性,代码如下:
public class FastDFSFile {
//文件名字
private String name;
//文件内容
private byte[] content;
//文件扩展名
private String ext;
//文件MD5摘要值
private String md5;
//文件创建作者
private String author;
public FastDFSFile(String name, byte[] content, String ext, String height,
String width, String author) {
super();
this.name = name;
this.content = content;
this.ext = ext;
this.author = author;
}
public FastDFSFile(String name, byte[] content, String ext) {
super();
this.name = name;
this.content = content;
this.ext = ext;
}
// getter and setter ...
}
文件操作:
创建FastDFSClient类,放在com.itheima.file.util下在该类中实现FastDFS信息获取以及文件的相关操作
package com.changgou.file.utils;
import com.changgou.file.pojo.FastDFSFile;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* 文件上传的工具类
*/
public class FastDFSClient {
private static Logger logger = LoggerFactory.getLogger(FastDFSClient.class);
private static TrackerClient trackerClient = null;
private static StorageClient1 storageClient1 = null;
private static TrackerServer trackerServer = null;
private static StorageServer storageServer = null;
//静态代码块 为了上传文件 初始化加载文件的上传配置文件 (文件中包含 上传文件的地址 连接时间 读取时间 编码集 等
static {
try {
String absolutePath = new ClassPathResource("fdfs_client.conf").getFile().getAbsolutePath();
ClientGlobal.init(absolutePath);
trackerClient = new TrackerClient();
trackerServer = trackerClient.getConnection();
storageClient1 = new StorageClient1(trackerServer, storageServer);
} catch (Exception e) {
e.printStackTrace();
logger.error("ClientGlobal is fail:{}",e.getMessage());
}
}
/**
* 上传文件
*
* @param file
* @return 文件的路径
*/
public static String uploadFile(FastDFSFile file){
System.out.println("上传文件");
NameValuePair[] nameValuePairs = new NameValuePair[1];
nameValuePairs[0] = new NameValuePair(file.getAuthor());
try {
return storageClient1.upload_file1(file.getContent(),file.getExt(),nameValuePairs);
} catch (Exception e) {
e.printStackTrace();
logger.error("upload file is fail: {} ",e.getMessage());
}
return null;
}
/**
* 下载文件
* @param path
* @return 文件对象
*/
public static InputStream downFile(String path){
try {
byte[] bytes = storageClient1.download_file1(path);
return new ByteArrayInputStream(bytes);
} catch (Exception e){
e.getStackTrace();
logger.error("download file is fail : {}" ,e.getMessage());
}
return null;
}
/**
* 删除文件
* @param path
*
*/
public static void deleteFile(String path){
try {
storageClient1.delete_file1(path);
} catch (Exception e) {
e.printStackTrace();
logger.error("delete file is fail : {}",e.getMessage());
}
}
/***
* 获取Tracker服务地址
* @return
* @throws IOException
*/
public static String getTrackerUrl() throws IOException {
return "http://"+trackerServer.getInetSocketAddress().getHostString()+":"+ClientGlobal.getG_tracker_http_port()+"/";
}
}
文件上传:
package com.changgou.file.controller;
import com.changgou.entity.Result;
import com.changgou.entity.StatusCode;
import com.changgou.file.pojo.FastDFSFile;
import com.changgou.file.util.FastDFSClient;
import org.apache.commons.io.FilenameUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
/**
* 文件管理
*/
@RestController
@RequestMapping("/file")
@CrossOrigin
public class FileController {
//打印日志
private Logger logger = LoggerFactory.getLogger(FileController.class);
/**
* 上传文件
* @param file
* @return 返回对象
*/
@PostMapping("/uploadFile")
public Result uploadFile(MultipartFile file){
//1:创建文件对象
try {
FastDFSFile fastDFSFile = new
FastDFSFile(file.getOriginalFilename(),file.getBytes(), FilenameUtils.getExtension(file.getOriginalFilename()));
//2:上传文件
String path = FastDFSClient.uploadFile(fastDFSFile);
//3:获取文件的访问路径
return new Result(true, StatusCode.OK,"上传成功",FastDFSClient.getTrackerUrl() + path);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
注意:文件上传,先传到控制器,然后才由控制器传入到FastDFS