传统文件系统
缺点
回顾玩servlet时的文件上传和下载
文件上传
假如前端轰HTML写法是如下的样子:
<div id="image">
<label for="">标题图片:</label>
<input type="file" id="file" name="file" >
<img src="" alt="" width="100px" height="150px">
</div>
JS写法如下:
// 当图片发生改变时 —— 也就是用户点击file框,上传文件时
$("#file").on( 'change' , function () { // 创建一个FormData空对象,就相当于是伪造了一个form表单
let formData = new FormData();// 这个FromData对象就用来装文件内容
// 文件的files属性本质是个数组
let files = $("#file").prop("files");
formData.append("upFile" , files[0] );
$.ajax( {
url: '/ajax/upload.do',
type: 'post',
data: formData,
dataType: 'json',
cache: false, // 上传文件不需要缓存
contentType: false, // 不需要对内容类型进行处理 因为内容是一个FormData对象
processData: false, // 不需要对数据进行处理,因为上面的data是一个FormData对象
// 后台返回的格式 :
// { "errno":"0" , "data":[ {"alt":"1633528500498.jpg" , "url":"/upload/2021-10-06/1633528500498.jpg"} ] }
success: function (info) {
info.data.forEach( function (data) {
// $("#image img").remove();
// $("#image").append( ' &lt;img src=" '+data.url+' " alt="" width="100px" height="150px"&gt; ' )</code></pre>/*
注掉的这种是:html中没有img标签时使用
因为:使用下面这种方法的情景是 —— 页面本来就有一个img框( 即:初始页面上这个file本身有一张图片 ),所以下面这种可以做到图片改变时把图片的路径换掉,也就是图片渲染( 也是数据回填 的思想 )
但是:如果页面一开始file的位置是不应该有图片的,是后面用户选了之后才出现图片预览效果,那么:就使用注释掉的这种方法:追加
*/ $("#image img").attr("src" , data.url );
});
}
} );
})</code></pre></li>
那么后端的low代码如下:
import com.alibaba.fastjson.JSON;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.File;
import java.io.IOException;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
// @MultipartConfig 注解就是文件注解,要获取前端的文件信息,必须加这个注解,不然做的所有事情都是无用功
@MultipartConfig
@WebServlet("/ajax/upload.do")
public class UploadServlet extends HttpServlet {@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {/*
* 想要构建的是这么一个玩意儿
* "errno":0 data:[ { url:"图片地址“ } , { alt:"图片说明“ } , { href:"null" } ]
*
* */
ArrayList&lt;Object&gt; list = new ArrayList&lt;&gt;();
Collection&lt;Part&gt; parts = req.getParts(); // 这是获取前台上传的文件
for (Part part : parts) {
// 先构建 data:[ { } , { } ]中的[ { } , { } ]
// 获取文件的全路径
// 但是:不同浏览器的这个全路径都不一样,所以需要截取从而自定义文件名
String filePath = part.getSubmittedFileName();
// System.out.println(filePath);
// 截取文件的后缀名
int subFileName = filePath.lastIndexOf(".");
String fileSuffix = filePath.substring(subFileName);
// 自己给文件重新定义一个名字,并规定存放的地方
String timeStr = LocalDate.now().toString();
// 获取当前项目的一个指定文件夹名字,用来保存文件 注意:getRealPath这是获取的当前项目的全路径,即:从盘符开始的路径
String proPathName = this.getServletContext().getRealPath("/upload/" + timeStr );
File file = new File(proPathName);
if ( !file.exists() ){
file.mkdirs();
}
// 拼接文件后缀名并保存文件
long timeStamp = new Date().getTime();
part.write(proPathName + "/" + timeStamp + fileSuffix );
HashMap&lt;String, String&gt; map = new HashMap&lt;&gt;();
map.put( "url" , "/upload/" + timeStr + "/" + timeStamp + fileSuffix );
map.put( "alt" , timeStamp + fileSuffix );
map.put( "href" , null );
list.add(map);
}
// 再构建"errno":0 data:[ { url:"图片地址“ } , { alt:"图片说明“ } , { href:"null" } ]
HashMap&lt;String, Object&gt; map = new HashMap&lt;&gt;();
map.put("errno", "0");
map.put("data", list);
resp.getWriter().print( JSON.toJSONString(map) );
}
}
文件下载
后端low代码如下
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;
@WebServlet("/downFile")
public class downFileInClientServlet extends HttpServlet {@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1、获取让浏览器下载的文件路径
String FileRealPath = "D:\\JavaTrainStudy\\servlet\\out\\production\\study06-httpServletResponse\\loginbg.png";
// 2、告知浏览器要下载的文件名是什么?
String fileName = FileRealPath.substring( FileRealPath.lastIndexOf("\\") + 1 );
// 3、让浏览器支持文件下载
// Content-Disposition这个就是让浏览器支持文件下载
// URLEncoder.encode( String s , String enc ) 是为了以防文件名是中文名,这样就设置编码格式了,让浏览器能够解析这个中文文件名
resp.setHeader("Content-Disposition" , "attachment ; filename=" + URLEncoder.encode(fileName , "utf-8"));
// 4、获取输入、输出流对象 并 把服务器中的文件输出到浏览器上
FileInputStream fis = new FileInputStream( FileRealPath );
ServletOutputStream os = resp.getOutputStream();
// 创建缓冲区
int len = 0 ;
byte[] buffer = new byte[1024];
while ( ( len = fis.read( buffer ) ) > 0 ){
os.write( buffer , 0 , len);
}
// 5、关闭流管道
if ( os != null ){
os.close();
}
if ( fis != null ){
fis.close();
}
}
}
分布式文件系统
优点
补充:常见的分布式文件系统
FastDFS是一个开源的轻量级分布式文件系统,为互联网应用量身定做,简单、灵活、高效,采用C语言开发,由阿里巴巴开发并开源
FastDFS对文件进行管理,功能包括:文件存储、文件同步( 指的是:文件系统 和 数据备份之间的同步 )、文件上传、文件下载、文件删除等
FastDFS解决了大容量文件存储的问题
FastDFS特别适合以文件为载体的在线服务,如相册网站、文档网站、图片网站、视频网站等
FastDFS充分考虑了冗余备份、线性扩容等机制,并注重高可用、高性能等指标,使用FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务
冗余备份:指的是文件系统中存的文件 和 数据备份中存的文件完全一致的问题
线性扩容:文件系统 和 数据备份不断增加呗( 就是上图中再加几份嘛 ),和水平扩容类似
由两大部分构成,一个是客户端,一个是服务端
注:我的系统是centos 7
安装需要的依赖环境 gcc、libevent、libevent-devel
yum install gcc libevent libevent-devel -y
安装公共函数库libfastcommon 和 fastDFS压缩包
加压公共函数库libfastcommon
tar -zxvf libfastcommon-1.0.36.tar.gz
进入libfastcommon,执行里面的make.sh,编译公共函数
./make.sh
./make.sh && ./make.sh install
安装公共函数
./make.sh install
解压缩fastdfs-5.11.tar.gz
压缩包
tar -zxvf fastdfs-5.11.tar.gz
进入解压之后的文件,使用make sh
进行编译
./make.sh
./make.sh && ./make.sh install
安装
./make.sh install
检查是否安装成功,进入如下的目录即可
cd /usr/bin
往后找,出现这些fdfs开头的文件就表示成功( 这些文件就是fastDFS的相关命令 )
fastDFS配置文件所在地,进入如下目录即可
想要让fastDFS的配置文件生效,那么就需要放到下面的这个目录中
cd /etc/fdfs
拷贝两个配置文件到etc/fdfs
中,这两个配置文件在解压的fastDFS的conf中,一个叫http.conf
,一个叫mime.types
cp http.conf /etc/fdfs
cp mime.types /etc/fdfs
以防万一,因此:将上面的文件拷贝一份
/etc/fdfs
mv storage.conf.sample ./storage.conf
mv tracker.conf.sample ./tracker.conf
在这个配置文件中有一个base_path
配置,指向的是fastDFS作者余庆的地址,而我们自己的linux中并没有这个目录,因此:做修改
vim tracker.conf
/base_path
base_path=/opt/fastdfs/tracker
注意:需要保证这个目录必须存在,没存在那就需要创建
需要改的内容如下
base_path=/opt/fastdfs/storage
store_path0=/opt/fastdfs/storage/files
tracker_server=服务器ip:22122
注意:要是前面的那三个目录没有的话,记得创建,若指向的是已经创建好的目录,那就不用创建了
mkdir -p /opt/fastdfs/tracker
mkdir -p /opt/fastdfs/storage
mkdir -p /opt/fastdfs/storage/files
在任意目录下,执行如下的命令即可
fdfs_trackerd /etc/fdfs/tracker.conf
fdfs_storaged /etc/fdfs/storage.conf
查看是否启动成功
ps -ef | grep fdfs
如下图表示启动成功
但是上面的启动会有坑儿,所以需要确认一把
cd /opt/fastdfs/storage/logs/storage.log
若是发现日志中报的是如下信息
ERROR - file: storage_ip_changed_dealer.c, line: 180, connect to tracker server 服务器ip:22122 fail, errno: 110, error info: Connection timed out
即:链接超时
这种情况一般都是22122端口没开放
firewall-cmd --zone=public --add-port=22122/tcp --permanent
systemctl restart firewalld.service
进入如下的目录
cd /opt/fastdfs/storage/files/data
这里面有526个文件夹,而每一个文件夹里面又有526个文件夹,即256 * 256个文件夹,总的文件夹数目为6万多个
要修改的内容如下:
base_path=/opt/fastdfs/client
tracker_server=自己服务器ip:22122
搞一个用来测试上传的文件
执行文件上传命令
可以使用如下命令看一下测试文件上传命令是怎么写的
提取出测试命令语法
fdfs_test
operation: upload, download, getmeta, setmeta, delete and query_servers
fdfs_test /etc/fdfs/client.conf upload /root/hello-fastdfs.txt
注意:防火墙的问题啊,要是报:connect to 162.14.66.60:23000 fail, errno: 113, error info: No route to host
,这就是防火墙没开放23000端口,打开就可以了
firewall-cmd --zone=public --add-port=23000/tcp --permanent
systemctl restart firewalld.service
上面成功之后有一堆信息,很重要
This is FastDFS client test program v5.11
Copyright (C) 2008, Happy Fish / YuQing
FastDFS may be copied only under the terms of the GNU General
Public License V3, which may be found in the FastDFS source kit.
Please visit the FastDFS Home Page http://www.csource.org/
for more detail.
[2022-06-01 10:17:07] DEBUG - base_path=/opt/fastdfs/client, connect_timeout=30, network_timeout=60, tracker_server_count=1, anti_steal_token=0, anti_steal_secret_key length=0, use_connection_pool=0, g_connection_pool_max_idle_time=3600s, use_storage_id=0, storage server id count: 0
tracker_query_storage_store_list_without_group:
server 1. group_name=, ip_addr=162.14.66.60, port=23000
group_name=group1, ip_addr=162.14.66.60, port=23000
storage_upload_by_filename
group_name=group1, remote_filename=M00/00/00/CgAAEGKWzCOACGE1AAACgUQE2TQ590.txt
source ip address: 10.0.0.16
file timestamp=2022-06-01 10:17:07
file size=641
file crc32=1141168436
example file url: http://162.14.66.60/group1/M00/00/00/CgAAEGKWzCOACGE1AAACgUQE2TQ590.txt
storage_upload_slave_by_filename
group_name=group1, remote_filename=M00/00/00/CgAAEGKWzCOACGE1AAACgUQE2TQ590_big.txt
source ip address: 10.0.0.16
file timestamp=2022-06-01 10:17:07
file size=641
file crc32=1141168436
example file url: http://162.14.66.60/group1/M00/00/00/CgAAEGKWzCOACGE1AAACgUQE2TQ590_big.txt
单独说明:remote_filename
remote_filename=M00/00/00/
M00 指的是:/opt/fastdfs/storage/files/data 就是前面去看默认创建文件数( 256 * 256 )的位置,跟前面的配置有关啊
00/00/ 指的就是:/opt/fastdfs/storage/files/data目录下的00子目录,这里面的00目录
CgAAEGKWzCOACGE1AAACgUQE2TQ590.txt 指的是:保存的文件名 fastdfs会重新生成文件名,以防的就是同名文件,造成附件覆盖的问题
上图中几个文件解读
-rw-r--r-- 1 root root 641 Jun 1 10:17 CgAAEGKWzCOACGE1AAACgUQE2TQ590_big.txt
-rw-r--r-- 1 root root 49 Jun 1 10:17 CgAAEGKWzCOACGE1AAACgUQE2TQ590_big.txt-m
-rw-r--r-- 1 root root 641 Jun 1 10:17 CgAAEGKWzCOACGE1AAACgUQE2TQ590.txt
-rw-r--r-- 1 root root 49 Jun 1 10:17 CgAAEGKWzCOACGE1AAACgUQE2TQ590.txt-m
前面已经见过对应的语法了
fdfs_test
operation: upload, download, getmeta, setmeta, delete and query_servers
变一下就可以了
fdfs_test /etc/fdfs/client.conf download
fdfs_test
fdfs_test /etc/fdfs/client.conf download group1 M00/00/00/CgAAEGKWzCOACGE1AAACgUQE2TQ590.txt
fdfs_test /etc/fdfs/client.conf delete group1 M00/00/00/CgAAEGKWzCOACGE1AAACgUQE2TQ590.txt
以上这些fdfs_test
只会在测试时使用,其他地方基本上都不用的
上传fastdfs-niginx
扩展模块 并 解压 - 使用官网中wiki说明的命令拉取也行
安装nginx,要是有的话就跳过
注意点:nginx
和fastdfs-nginx
放到/usr/local
目录下,不然可能会出现莫名其妙的问题
记住两个目录
/usr/local/nginx_fdfs
/usr/local/fastdfs-nginx-module-master/src
进入nginx安装目录,进行模块添加配置
cd nginx_fdfs
./configure --prefix=/usr/local/nginx_fdfs --add-module=/usr/local/fastdfs-nginx-module-master/src
编译并安装
make & make install
注释事项:Nginx的安装需要Linux安装相关的几个库,否则编译会出现错误,有这几个的话就不安装了
yum install gcc openssl openssl-devel pcre pcre-devel zlib zlib-devel –y
将fastdfs-nginx扩展模块中的mod_fastdfs.conf
文件复制到/etc/fdfs
中
cp /usr/local/fastdfs-nginx-module-master/src/mod_fastdfs.conf /etc/fdfs
修改/etc/fdfs/mod_fastdfs.conf
vim mod_fastdfs.conf
base_path=/opt/fastdfs/nginx_mod
tracker_server=自己服务器ip:22122
url_have_group_name = true
store_path0=/opt/fastdfs/storage/files
上面base_path
目录要是不存在记得创建
进入nginx_fdfs
的安装目录中,去nginx.conf
中配置fastdfs-nginx
的扩展模块
vim /usr/local/nginx_fdfs/conf/nginx.conf
location ~ /group[1-9]/M0[0-9] {
ngx_fastdfs_module;
}
启动nginx
/usr/local/nginx_fdfs/sbin/nginx -c /usr/local/nginx_fdfs/conf/nginx.conf -t
/usr/local/nginx_fdfs/sbin/nginx -c /usr/local/nginx_fdfs/conf/nginx.conf
ps -ef | grep nginx
注意:这里很容易出现启动不起来,如果下面这个进程没有启动起来
nobody 3895 3894 0 15:45 ? 00:00:00 nginx: worker process
那么:就去查看日志文件
cd /usr/local/nginx_fdfs/logs
cd /opt/fastdfs/nginx_mod
在resources
目录下创建fastdfs.conf
文件,并编写如下内容:
tracker_server=服务器ip:22122
编写文件上传代码
package com.zixieqing;
import org.csource.common.MyException;
import org.csource.fastdfs.*;
import java.io.IOException;
/**
public class UploadFile {
public static void main(String[] args) {TrackerServer trackerServer = null;
StorageServer storageServer = null;
try {
// 1、初始化配置文件
ClientGlobal.init("fastdfs.conf");
// 2、创建tracker客户端
TrackerClient trackerClient = new TrackerClient();
// 3、获取trackerServer
trackerServer = trackerClient.getConnection();
// 4、获取storageServer
storageServer = trackerClient.getStoreStorage(trackerServer);
// 5、创建storage客户端 - 这个对象就是用来上传文件、下载文件、删除文件的
StorageClient storageClient = new StorageClient(trackerServer, storageServer);
// 6、上传文件
/*
这里有两个API需要了解
String[] upload_file(byte[] file_buff, int offset, int length, String file_ext_name, NameValuePair[] meta_list)
这个API常用来web中上传文件的
参数1 file_buff、文件字节
offset、length、从文件的那个位置开始上传,截止位置
参数4 file_ext_name、文件后缀
参数5 meta_list、文件的属性文件
String[] upload_file(String local_filename, String file_ext_name, NameValuePair[] meta_list)
这个API是上传本地文件的
参数1 local_filename、本地文件的绝对路径
参数2 file_ext_name、文件后缀名
参数3 meta_list、文件的属性文件,linux1中的哪个meta data,一般都不传
上述这两个API,注意返回值,这个String[] 很重要,就是涉及到linux中的那个组名group 和 远程文件名remote_filename
*/
String[] result = storageClient.upload_file("C:\\Users\\ZiXieQing\\Desktop\\图库\\19.jpg", "jpg", null);
// 7、验证一下
for (String data : result) {
System.out.println("data = " + data);
}
} catch (IOException e) {
throw new RuntimeException(e);
} catch (MyException e) {
throw new RuntimeException(e);
} finally {
// 8、释放资源
if (storageServer != null) {
try {
storageServer.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if (trackerServer != null) {
try {
trackerServer.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
把前面的文件上传代码改一下即可,换成另一个API而已
package com.zixieqing;
import org.csource.common.MyException;
import org.csource.fastdfs.*;
import java.io.IOException;
/**
public class DownloadFile {
public static void main(String[] args) {TrackerServer trackerServer = null;
StorageServer storageServer = null;
try {
// 1、初始化配置文件
ClientGlobal.init("fastdfs.conf");
// 2、获取tracker客户端
TrackerClient trackerClient = new TrackerClient();
// 3、获取trackerServer
trackerServer = trackerClient.getConnection();
// 4、获取storageServer
storageServer = trackerClient.getStoreStorage(trackerServer);
// 5、创建storage客户端
StorageClient storageClient = new StorageClient(trackerServer, storageServer);
// 6、下载文件
/*
这里需要知道两个API
byte[] download_file(String group_name, String remote_filename)
这个API常用于web操作
int download_file(String group_name, String remote_filename, String local_filename)
这个API是把文件下载到本地磁盘中
这个API的返回值结果很重要
*/
String group = "group1";
String remoteFileName = "M00/00/00/CgAAEGKYRg-AAIrWAAD8cA4U6dY771.jpg";
// 存入本地磁盘路径+存入磁盘的文件名
String localFileName = "d:/靓妹.jpg";
// 只有返回值是0才表示下载成功,否则只要是其他数字都是下载失败( 其他数字有可能是组名错了,远程文件名错了........
int result = storageClient.download_file(group, remoteFileName, localFileName);
// 7、验证
System.out.println("result = " + result);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (MyException e) {
throw new RuntimeException(e);
} finally {
// 8、释放资源
if (storageServer != null) {
try {
storageServer.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if (trackerServer != null) {
try {
trackerServer.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
package com.zixieqing;
import org.csource.common.MyException;
import org.csource.fastdfs.*;
import java.io.IOException;
/**
* @author : ZiXieQing
* @version : V1.0.0
* @className : DeleteFile
* @description : 该类功能 FastDFS删除文件
* @packageName : com.zixieqing
*/
public class DeleteFile {
public static void main(String[] args) {
TrackerServer trackerServer = null;
StorageServer storageServer = null;
try {
// 1、初始化配置文件
ClientGlobal.init("fastdfs.conf");
// 2、获取tracker客户端
TrackerClient trackerClient = new TrackerClient();
// 3、获取trackerServer
trackerServer = trackerClient.getConnection();
// 4、获取storageServer
storageServer = trackerClient.getStoreStorage(trackerServer);
// 5、获取storage客户端
StorageClient storageClient = new StorageClient(trackerServer, storageServer);
// 6、执行文件删除
/*
int delete_file(String group_name, String remote_filename)
参数1 group_name、组名
参数2 remote_filename、远程文件名
*/
// 一样的,返回值是0就表示成功,其他都是删除失败
int result = storageClient.delete_file("group", "M00/00/00/CgAAEGKYRg-AAIrWAAD8cA4U6dY771.jpg");
// 7、验证一下
System.out.println("result = " + result);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (MyException e) {
throw new RuntimeException(e);
}finally {
// 8、释放资源
if (storageServer != null) {
try {
storageServer.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if (trackerServer != null) {
try {
trackerServer.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
手机扫一扫
移动阅读更方便
你可能感兴趣的文章