我们本是想实现商品添加这个功能的,但在实现该功能的过程中,发现还得实现两个功能,一是商品类目的选择,即商品属于哪个分类;一是图片上传。不久之前,我们实现了商品类目的选择这个功能,只不过现在是还没实现图片上传这个功能。
如何实现图片上传这个功能呢?本文就来告诉大家如何实现,前提是我们需要搭建一台图片服务器,专门用于保存图片。
温馨提示:淘淘商城这个项目将使用分布式文件系统FastDFS来搭建一台最简单的FastDFS图片服务器。
由于中央仓库并没有fastdfs_client的jar包,因此需要我们自己整。小朋友,你是不是有很多问号,该jar包从哪儿给整来呢?并且又该如何将这样的jar包给弄到本地仓库中去呢?所有这些疑问你都可以在我的《maven快速入门番外篇——Eclipse下载GitHub上FastDFS-Client客户端源码并转化成maven工程以及打包到本地maven仓库》这篇博客中找到答案。
要是你还是觉得像上面这样做很麻烦,我也觉得实在是麻烦,为了节省同学们的时间,这里我提供了一个有关fastdfs-client-java的maven工程,大家可以直接从我给出的百度网盘链接地址中进行下载。
链接:https://pan.baidu.com/s/16UPDNRPa65WdeI-POotyBA,提取码:d9ep
我们所要做的事就是将fastdfs-client-java这个maven工程导入到Eclipse中,导入完成之后记得要将这个maven工程打包到本地maven仓库中。
至此,是不是万事大吉了呢?很显然并不是,我们直接通过表现层上传图片到图片服务器,大可不必再经过service层,这样taotao-manager-web工程就需要依赖fastdfs-client-java工程了,因此我们需要在taotao-manager-web工程的pom文件中添加对fastdfs-client-java工程的依赖,如下所示。
<dependency>
<groupId>org.csourcegroupId>
<artifactId>fastdfs-client-javaartifactId>
<version>1.29-SNAPSHOTversion>
dependency>
首先,在taotao-manager-web工程的src/main/resources目录下新建一个resource文件夹并在它下面创建一个client.conf文件,client.conf文件中输入tracker所在的设备的IP地址及端口号,由于我的tracker是在IP地址为192.168.81.132的虚拟机上,因此我这里写的是tracker_server=192.168.81.132:22122
,如下图所示。
然后,我们新建一个测试类来进行图片上传的测试,我们在src/test/java目录下新建一个com.taotao.fastdfs包,并在该包下新建一个测试类,例如TestFastDFS.java,如下图所示。
为方便大家复制,现把以上测试类的代码粘贴如下:
package com.taotao.fastdfs;
import org.csource.fastdfs.ClientGlobal;
import org.csource.fastdfs.StorageClient;
import org.csource.fastdfs.StorageServer;
import org.csource.fastdfs.TrackerClient;
import org.csource.fastdfs.TrackerServer;
import org.junit.Test;
public class TestFastDFS {
@Test
public void uoloadFile() throws Exception {
// 1. 向工程中添加jar包(FastDFS客户端的jar包)
// 2. 创建一个配置文件,配置一下tracker服务器的地址
// 3. 写代码加载这个配置文件
ClientGlobal.init("D:/Practise/taotao/taotao-manager-web/src/main/resources/resource/client.conf");
// 4. 创建一个TrackerClient对象
TrackerClient trackerClient = new TrackerClient();
// 5. 使用这个TrackerClient对象获得一个TrackerServer对象
TrackerServer trackerServer = trackerClient.getTrackerServer();
// 6. 创建一个StorageServer的引用,null就可以了
StorageServer storageServer = null;
// 7. 创建一个StorageClient对象,创建这个对象时需要两个参数,一个是TrackerServer的引用,一个是StorageServer的引用
StorageClient storageClient = new StorageClient(trackerServer, storageServer);
// 8. 使用StorageClient对象来上传文件
/*
* upload_appender_file方法中的三个参数:
* 1. 参数一:文件名
* 2. 参数二:扩展名,没有包含"."
* 3. 参数三:文件的元数据,例如保存文件的原始名、大小、尺寸等,若没有则可为null
*/
String[] strings = storageClient.upload_appender_file("D:/picture/关于李阿昀/知乎图片/mei.jpg", "jpg", null);
for (String string : strings) {
System.out.println(string);
}
}
}
在编写以上代码时,你有两点需要注意:
taotao-manager-web工程还未添加对junit的依赖,因此需要在pom文件中添加对junit的依赖,如下所示,这里之所以不用写版本是因为在taotao-parent工程中已经统一定义好版本了。
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
dependency>
我们在复制粘贴Win10系统中的本地文件绝对路径(例如F:\fastdfs_test\meinv.jpg
)时,Eclipse是识别不了的,运行测试方法会报如下错误。
解决方法是手动输入一遍,而且要注意把包括双引号在内的这个路径串(F:\fastdfs_test\girl.jpg)删除,然后再手动输入一遍。
注意上面两点之后,我们执行测试类中的测试方法方可成功,如下图所示,回显信息的第一行是该图片被保存到哪个组了,由于我们现在只是用的单机版FastDFS服务器,因此现在都是group1;第二行是存放的具体位置。
既然图片上传上去了,现在我们试着用http的方式来访问下该图片,我们需要把group1和M00/00/00/wKhRhF8LqlmEFphwAAAAAHeCIYo003.jpg拼接到一块来访问,这样我们便可以查看到刚才上传的图片了,如下图所示。
经过上面的操作,说明我们搭建的图片服务器没任何问题。但有个问题是,图片上传的操作步骤蛮繁琐,因此我们迫切需要对其进行封装,现在我把封装好的一个工具类(即FastDFSClient.java)的代码粘贴如下。
package com.taotao.utils;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.ClientGlobal;
import org.csource.fastdfs.StorageClient1;
import org.csource.fastdfs.StorageServer;
import org.csource.fastdfs.TrackerClient;
import org.csource.fastdfs.TrackerServer;
public class FastDFSClient {
private TrackerClient trackerClient = null;
private TrackerServer trackerServer = null;
private StorageServer storageServer = null;
private StorageClient1 storageClient = null;
public FastDFSClient(String conf) throws Exception {
if (conf.contains("classpath:")) {
conf = conf.replace("classpath:", this.getClass().getResource("/").getPath());
}
ClientGlobal.init(conf);
trackerClient = new TrackerClient();
trackerServer = trackerClient.getTrackerServer();
storageServer = null;
storageClient = new StorageClient1(trackerServer, storageServer);
}
/**
* 上传文件方法
* Title: uploadFile
* Description:
* @param fileName 文件全路径
* @param extName 文件扩展名,不包含(.)
* @param metas 文件扩展信息
* @return
* @throws Exception
*/
public String uploadFile(String fileName, String extName, NameValuePair[] metas) throws Exception {
String result = storageClient.upload_file1(fileName, extName, metas);
return result;
}
public String uploadFile(String fileName) throws Exception {
return uploadFile(fileName, null, null);
}
public String uploadFile(String fileName, String extName) throws Exception {
return uploadFile(fileName, extName, null);
}
/**
* 上传文件方法
* Title: uploadFile
* Description:
* @param fileContent 文件的内容,字节数组
* @param extName 文件扩展名
* @param metas 文件扩展信息
* @return
* @throws Exception
*/
public String uploadFile(byte[] fileContent, String extName, NameValuePair[] metas) throws Exception {
String result = storageClient.upload_file1(fileContent, extName, metas);
return result;
}
public String uploadFile(byte[] fileContent) throws Exception {
return uploadFile(fileContent, null, null);
}
public String uploadFile(byte[] fileContent, String extName) throws Exception {
return uploadFile(fileContent, extName, null);
}
}
以上工具类的构造方法中有如下这样一行代码:
conf = conf.replace("classpath:", this.getClass().getResource("/").getPath());
这句代码的意思是,如果用户传入的文件路径是相对路径(相对路径以src/main/resources目录为根目录),比如用户传入的文件路径是classpath:applications.properties
,那么需要转为绝对路径,因此需要把classpath:
给替换掉,改为D:/Practise/taotao/taotao-manager-web/src/main/resources
。而且封装类中使用的Storage客户端是StorageClient1而不是StorageClient,这个客户端的好处是能够帮我们自动把文件所在的组以及存放位置拼接到一块。
接下来,我们就来测试一下这个工具类是否好使。首先在taotao-manager-web工程下新建一个com.taotao.utils工具包,然后把我们的封装类(即FastDFSClient.java)放到该包中,接着在TestFastDFS单元测试类中新建一个testFastDFSClient测试方法,如下图所示。
其中,testFastDFSClient单元测试方法的代码如下:
@Test
public void testFastDFSClient() throws Exception {
FastDFSClient fastDFSClient = new FastDFSClient("D:/Practise/taotao/taotao-manager-web/src/main/resources/resource/client.conf");
// 注意,你再上传一遍刚刚上传的图片,刚才你上传的那个图片就已经丢了,它已经存在服务器上了
String string = fastDFSClient.uploadFile("D:/picture/关于李阿昀/知乎图片/girl.jpg");
System.out.println(string);
}