FastDFS 实际开发中的应用(上传、下载、删除文件)

实际开发时 应用场景 :

(1)用户的头像 可以通过我们的 fastdfs 来存放进我们的 磁盘当中。

(2)我们需要看到 头像 怎么办呢: 头像其实就是img 标签,然后就可以写一个地址,然后让nginx来接收,访问上传的头像。

(3)客户端jar 就是一个maven项目,我们直接将它clean 然后 install 打包进我们的maven仓库中,在以后的 编写代码的时候字节通过maven引入依赖就行了。

(4)Maven工程找配置文件 是 自动去 resources中去找的。

上传文件的 五步固定套路:

上面这句代码会返回一个数组,数组中的两个返回值为:如下图:

我们将meta_list设置为null,fastDFS就不会为我们生成属性文件,也就是以-m结尾的文件。

在实际开发中我们是不需要生成-m属性文件的,因为占有效存储空间。

设置为 null 就不生成meta属性文件(-m)。

上传 ,下载,删除 文件 的 操作 :

下面两个条件 是用来配置 通过fastDFS存放文件的位置:

圆圈1 : 配置 当前的 组中 有几个存放 文件的位置。

圆圈2 : 配置这几个存放文件位置的路径。 如上图的 path0 路径,当我们运行fastDFS之后 ,fastDFS会为我们在 files目录下 创建一个 data 目录,这个data目录下 有256个目录,这 256 个目录 每个目录下又有256个目录,这256个目录才是真正存放文件的地方,这样子就形成了256*256的文件存放路径。

上面两行配置是用来干什么的?

上图 【两行配置】 只是用来配置在【组】中可以用来【存放文件】的【位置】的。 我们组数还是当前这个组,不会因为上面两行配置的配置而增加或者减少,增加或者减少的只是 组中 可以存放文件的位置而已。

什么是组(group)?

组这个词出现在 fastDFS给我们返回的访问路径中。【组】其实就是许多台服务器的集合,有一个专门用来存放文件的服务器,还有一个专门用来备份的服务器,所以说 一个组 中 最少有两台服务器,一个用来存取文件,一个用来备份文件的。

所以说: 组就是许多台服务器的集合 。

.conf  就是配置文件 :

.conf 是config的简写,也就是配置文件,多用于存取硬件驱动程序的安装配置信息。
内容一般是一些硬件的版本号呀,支持什么样的系统等信息。
本质上来说就是TXT文件,里面的格式没有统一标准,各个厂商的格式不同内容也完全不一样。

我们在配置文件中配置的是:

我们 java程序是通过jar包来连接服务端的 tracker ,然后我们的tracker又连接上了storage。

也就是说: 客户端的 java程序 是连接 tracker,服务端存放文件的storage也是连接tracker ,所以我们需要在配置文件中 配置 连接tracker的配置信息。

投资人 拥有债权,债权的意思就是投资人拥有催债的权利。一般 债权 的底下就 对应一笔借款。

扩展知识:

为什么需要在JDBC中添加 useSSL ?

用的是java-connector-5.1.42-bin.jar

当然结果是对的,但是上面一行说的什么useSSl没有设置,百度了一下,是这样的。

冷静分析:主要是我的jar包版本过高造成的。用以前的旧版本没什么问题,而且新版本的MySQL要求是否进行ssl连接。

  解决方法:这样写就可以了  String url = "jdbc:mysql://localhost:3306/school?useSSL=false";   

 

拓展:conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/Test_db?useUnicode=true&characterEncoding=utf-8&useSSL=false","root","password");

useUnicode设置主要是设置字符编码为utf-8,也可以在eclipse中设置默认的字符编码。

只要我们提交的表单中有附件的话,那么就需要添加一个属性:enctype。

只有表单中需要提交附件,那么就需要添加enctype这个属性。

这是一个名字叫做filename的 附件参数 ,注意是一个附件参数。

服务端控制层 应该怎样接收这个附件参数呢?

使用MultipartFile 这个对象来进行接收。 这个multipartfile类是我们的spring-boot框架为我们提供的,专门用来接收 【附件参数】的。

————————————————————————————————————————————————

我们的文件是通过什么样的形式 在 网络中进行传输的呢?

如果我们的文件需要在网络中进行传输,我们一般的做法是 将 文件 转换成 字节数组 进行传输。

<iframe>  和 <script> 标签 要以什么形式来结束标签 ?

<iframe> 和 <script>这两个标签 都要以</iframe>和</script> 这样来结束,千万不要以/斜杠这种形式来结束标签。

Form标签中的 target属性:

target 属性的意思是: form 表单 的 请求 从哪里进行提交。 可以在当前页进行提交,或者是新开一个新的页面进行提交,再或者从 iframe框架中进行提交。

我们有时候需要让我们的<form>表单 从 <iframe>内嵌页面中进行提交?

我们这样做的目的是为了【模拟ajax异步请求】,从而实现刷新。这种局部刷新的方法 在 很久以前没有ajax的时候 用来实现 异步请求局部刷新。

<form action=”/xxx/xxx/xxxx”  method=”post”  target=”target_iframe”>

</form>

<iframe name=”target_iframe”>

form表单会从 这个iframe中进行提交。

后端返回的 json 或 text 都会返回到这里,因为表单form中的请求 是从这里发出的。所以最终 后端 推送的 结果也会 推送到这个 iframe内嵌页面中 。

</iframe>

 

容易理解错的知识点:

<script></script>  和 js 代码。

我们的 js 代码如果没有写在方法中,也就是function 方法()  这里面,那么在加载页面的时候,我们的这些不在 方法中的js代码 就会 在读取页面的时候 执行和加载编译,也就是 页面只要加载 就会 执行这些不在方法中的js代码。如果 我们 将这些 js写在了方法中,也就是写在了 function 方法()中时,页面加载的时候 【不会】为我们加载编译和执行 写在方法中的js代码,如果我们想要执行:

JS代码执行的位置:

JS代码写在了 方法里面 :

<script>

function fun1(){

  一坨 JS 代码 : 巴拉巴拉。

}

fun1();  //页面加载到这里 会帮我们执行 js代码,执行写在方法中的js代码。

</script>

JS代码写在了 方法里面:

<script>

function fun1(){

  一坨 JS 代码 : 巴拉巴拉。 //这段js代码直接不执行了。

}

</script>

JS代码写在了 方法外面:

<script>

 一坨 JS 代码 : 巴拉巴拉。 //页面加载到这里就执行js代码了。

</script>

为什么会出现xml 映射文件没有编译到classPath下?

因为我们idea在进行编译的时候,resources目录下idea认为是配置文件,java目录下idea认为是java文件,所以在编译的时候 resources中只会编译配置文件,当然也会编译 我们的 thymeleaf 模板页面,一位内 我们 templates 目录是 创建 spring-boot项目的时候自动为我们创建的,所以会被编译。 但是我们mapper目录下的 .xml文件 不是java文件,所以我们的idea不会帮我们 继续编译,所以会出现 xml映射文件没有被变异的情况,所以说,我们需要配置resources,让我们的 xml映射文件被编译到我们的 classPath目录下。

如果xml映射文件没有编译到classpath目录下,我们浏览器会报出什么错误:

 【上传文件】 和 【下载文件】 中 涉及的 文件传输 和 文件参数的接收,文件在前端、传输中、后端的存在形式。

   (1)我们使用 form表单 将 我们的 选中的文件 提交到后台:

   代码: 

有上面 【两个黄线】  才能正常的用 表单提交我们的附件。

上面这段代码中,form表单就是从iframe框架中进行提交的,然后最后服务端将处理结果向前端进行推送的时候也是推送到 iframe 框架中,因为是 iframe框架 发出的 form表单的请求,所以最后也是iframe进行请求的接收。

控制层controller如何接收 请求中的附件参数?

  1. 下载文件:

将我们 通过 fastDFS软件 获取到的 文件 推送给我们浏览器,然后让我们的浏览器知道 推送过来的是一个附件,而且这个附件存在的形式是 字节流的形式,因为我们将文件转换成了  字节数组了,然后也告诉我们浏览器一个 我们自定义的这个文件的名称。 我们 向前端浏览器推送 附件的时候,然后让浏览器开始下载该文件,我们需要告诉我们的浏览器什么: 需要告诉我们的浏览器:这个数据是 【一个附件】,这个附件存在的形式为【字节流的形式】,因为我们之前将我们文件转换成了【字节数组】,而且我们还要告诉我们的浏览器 一个下载文件是 所要使用的文件名称。 这样子就可以让我们的浏览器在知道是一个文件之后,自动下载我们的文件。

@Controller
public class UserController {

    @Autowired
    private UserService userService ;

    @GetMapping(value="/fastdfs/downLoad")
    public ResponseEntity<byte[]> downLoad(
            @RequestParam(value="id") Integer id,
            Model model
    ){
        TrackerClient trackerClient = null ;
        TrackerServer trackerServer = null ;
        StorageServer storageServer = null ;
        StorageClient storageClient = null ;

        ResponseEntity<byte[]> responseEntity = null ;
        try{
            ClientGlobal.init("配置文件名");
            trackerClient = new TrackerClient();
            trackerServer = trackerClient.getConnection();
            storageServer = trackerClient.getStoreStorage(trackerServer);
            storageClient = new StorageClient(trackerServer,storageServer);

            //从数据库中获取到 当前 id 对应的数据库的 借款人信息 。
            Creditor creditor = userService.queryByPrimaryKey(id);
            String groupname = creditor.getGroupname();
            String remotefilepath = creditor.getRemotefilepath();
            byte[] fileBytes = storageClient.download_file(groupname, remotefilepath); //文件的字节数组存在形式。
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            String filePath = creditor.getRemotefilepath();
            String suffix = filePath.substring(filePath.indexOf("."));
            headers.setContentDispositionFormData("attachment",System.currentTimeMillis()+suffix);
    responseEntity = new ResponseEntity<byte[]>(fileBytes,headers,HttpStatus.OK);
        }catch(Exception e) {
            e.printStackTrace();
        }finally {
            if(null != trackerServer){
                try {
                    trackerServer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(null != storageServer){
                try {
                    storageServer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return responseEntity;
    }

 

我们 的 contoller控制层吗,需要给我们的前台的浏览器传递一个 【文件类型数据】,这个文件类型的数据,和平时向前段推送的 json ,string ,view 等等类型数据都不一样,因为这是向 前台浏览器推送 一个 文件,当我们浏览器接收到这个 文件类型的数据的时候就会 弹出下载框,进行下载操作。 所以我们需要告诉我们的浏览器 我们向他 推送的是一个文件,而且告诉我们的 浏览器 我们这个文件的 【存在的形式】 和 【文件的名字】。

这样我们的浏览器就会自动弹出下载框了。

需要告诉我们的 浏览器 那些信息:【这个数据是一个文件】,【文件存在的形式:流形式】,【文件的名字】。

 HttpHeaders headers = new HttpHeaders(); 征税创建一个请求头,我们从请求头中告诉我的浏览器  我们 文件的内容 是什么形式的,因为我们的文件需要在网络中进行传递所以我们的文件需要转换成 【字节数组】,所以文件中内容的形式是 流形式,设置如下:
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);

然后 告诉我们的 浏览器 这个数据是 一个 文件,和这个文件的名字:

headers.setContentDispositionFormData("attachment",System.currentTimeMillis()+suffix);

这个对象是专门用来 向前台浏览器推送我们的 文件 数据的 :

 responseEntity = new ResponseEntity<byte[]>(fileBytes,headers,HttpStatus.OK);
向前台推送数据的时候 方法名前面不需要添加 @responsebody,因为这是推送文件,有点特殊。

 

综上所述:

上传文件和下载文件所涉及的 对象 和 方法 :

 

使用form表单,通过参数的形式 将 我们的文件,提交到后台的控制层:

Form表单标签中 需要添加 encTyle 属性:

 enctype="multipart/form-data"

 

控制层 的 接收方法中使用 MultipartFile 对象形参来接收请求中的 文件参数。

 

因为 文件的 传输是 跨网络的 ,所以 我们传输文件的时候 需要先将文件转换成 【字符数组】的形式进行传输。 byte[] 。

下载文件是:我们需要用responseEntity<byte[]> 对象 将我们的 字节数组形式的文件传递到 前台的浏览器。

 HttpHeaders 这个对象是用来这是响应头的,我们需要在响应头中 设置 文件 内容的 样式,因为我们之前为了传输文件,将文件转换成了字节数组的样式,所以如果 我们需要告诉我们的浏览器 后端给他推送 的   这个文件 的内容 是 什么 格式 什么样式的。 我们还需要告诉浏览器 这个数据是一个 附件,还要告诉它这个文件的名字。 需要告诉浏览器的这几个 数据 我们都存放在了 header响应头中了。

responseEntity<byte[]>对象的构造方法需要我们传递三个参数,分别为:文件的 byte[]格式,header响应头,通信状态码为OK 。这个responseEntity<byte[]>对象到达被推送到 浏览器之后,我们的浏览器就会自动的  弹出下载框了。

关于文件的 上传 和  下载  的 对象 :

EncType : 告诉form表单提交的参数中有附件。

MultiPartFile : 是spring-boot专门给我们提供接收请求中的 附件参数的 。

HttpHeaders : 响应头,我们可以设置: 文件内容的格式,推送数据类型,文件的名字 。

ResponseEntity<byte[]>: 这个是专门用来 将我们的 附件字节数组 推送到 前台的浏览器,然后浏览器 接收到 responseEntity中的 byte,header还有 状态码,就会直接弹出 下载框。

 

Enctype是 表单标签form的属性: encode + type ,编码样式。提交附件的时候需要指定一下。

接收页面请求中的参数:

文件在网络中进行传输,必须将文件转换成字节码数组。

我的js代码,没有写到方法里面,所以自动执行了,写到方法里面就需要手动调用了一下方法,然后执行代码。

当有一段js代码推送到我们的 iframe中是怎么 执行的?

因为我们的form表单设置的从 iframe框架中进行提交的,所以最后推送数据的时候也是向iframe框架进行推送数据:

当上面的这段string字符串推送到了我们的iframe框架中,就相当于是:

<iframe name="target_iframe" style="display: none;">
    <script>xxxxxx</script>
</iframe>

因为script中的代码没有写在方法当中,所以直接就被执行了,如果写在了方法当中,那么还需要调用一下方法来执行。

 

我们在响应头中设置 【文件内容格式】的时候通常有两种选择,这个格式是到了浏览器中后给我们【展示】的文件内容的格式:

创建一个响应头 : HttpHeaders headers = new HttpHeaders();
我们可以使用下面这段代码来设置 文件内容格式:headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);

最常用的两种格式:
(1)MediaType.APPLICATION_OCTET_STREAM   
文件内容是流形式的,因为我们的文件需要在网络中进行传输,所以文件内容需要转换成流形式进行传递。 


(2)MediaType.APPLICATION_JSON_UTF8
文件内容 是 json格式的,就是将我们的文件内容转换成了 json格式的字符串。 

返回的是一个文件,应该怎么操作:

我们不用写 responseBody了,因为我们已经告诉我们的浏览器我们返回的是一个什么类型的数据了,从响应头中告诉的。

我们设置一下 返回的内容的类型 和 返回的是什么一个什么类型的文件和数据。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值