來自 | ITDragon龍
鏈接 | cnblogs.com/itdragon/p/7864916.html
本章內容通過Nginx 和 FTP 搭建圖片服務器。在學習本章內容前,請確保您的Linux 系統已經安裝了Nginx 和 Vsftpd。
Nginx 安裝
http://www.cnblogs.com/itdragon/p/7850985.html
Vsftpd 安裝
http://www.cnblogs.com/itdragon/p/7857649.html
本章知識點
效果圖:
需求:實現圖片的上傳和批量上傳
技術:Nginx,Vsftpd,Spring,SpringMVC,KindEditor,CentOS
說明:本章節內容主要是實現圖片的上傳功能。使用 KindEditer 是為了更好的演示圖片的上傳,回顯,批量效果。後臺代碼與KindEditer沒有直接關係,放心閱讀。另外源碼中有Mybatis的jar,不用理會,本章內容用不到,是為後續內容做準備!
源碼:見文章底部
場景:用戶將圖片上傳到 tomcat 服務器上,再由 tomcat 服務器通過FTP上傳到 Nginx 服務器上。
項目結構:
單元測試
首先要攻破核心技術。通過單元測試實現圖片上傳的功能。
<code>public class PictureFTPTest {/<code>
<code> // 測試 ftp 上傳圖片功能/<code><code> @Test/<code><code> public void testFtpClient throws Exception {/<code><code> // 1. 創建一個FtpClient對象/<code><code> FTPClient ftpClient = new FTPClient;/<code><code> // 2. 創建 ftp 連接/<code><code> ftpClient.connect("192.168.0.11", 21);/<code><code> // 3. 登錄 ftp 服務器/<code><code> ftpClient.login("ftpuser", "root");/<code><code> // 4. 讀取本地文件/<code><code> FileInputStream inputStream = new FileInputStream(new File("F:\\\\hello.png"));/<code><code> // 5. 設置上傳的路徑/<code><code> ftpClient.changeWorkingDirectory("/usr/local/nginx/html/images");/<code><code> // 6. 修改上傳文件的格式為二進制/<code><code> ftpClient.setFileType(FTP.BINARY_FILE_TYPE);/<code><code> // 7. 服務器存儲文件,第一個參數是存儲在服務器的文件名,第二個參數是文件流/<code><code> ftpClient.storeFile("hello.jpg", inputStream);/<code><code> // 8. 關閉連接/<code><code> ftpClient.logout;/<code>
<code> }/<code>
<code>}/<code>
說明:這裡的ip地址,端口,ftp用戶名,密碼,本地文件路徑,以及Nginx服務器圖片路徑等,這些字符串參數都要根據自己實際設置的來填寫的。如果你的Nginx和Vsftpd安裝是按照我提供的鏈接來做的。那你只需要改ip地址即可。
Maven 的Web 項目
搭建Maven的Web 項目,之前有寫過。這裡就不過多描述。
項目核心配置文件
首先是 Maven 的核心文件 pom.xml,添加下列對應版本的依賴即可,可前去https://mvnrepository.com/tags/maven 查找依賴。
<code> <junit.version>4.12/<junit.version>/<code><code> <spring.version>4.1.3.RELEASE/<spring.version>/<code><code> <mybatis.version>3.2.8/<mybatis.version>/<code><code> <mybatis.spring.version>1.2.2/<mybatis.spring.version>/<code><code> <mybatis.paginator.version>1.2.15/<mybatis.paginator.version>/<code><code> <mysql.version>5.1.6/<mysql.version>/<code><code> <slf4j.version>1.6.4/<slf4j.version>/<code><code> <jackson.version>2.4.2/<jackson.version>/<code><code> <druid.version>1.0.9/<druid.version>/<code><code> <httpclient.version>4.3.5/<httpclient.version>/<code><code> <jstl.version>1.2/<jstl.version>/<code><code> <servlet-api.version>2.5/<servlet-api.version>/<code><code> <jsp-api.version>2.0/<jsp-api.version>/<code><code> <joda-time.version>2.5/<joda-time.version>/<code><code> <commons-lang3.version>3.3.2/<commons-lang3.version>/<code><code> <commons-io.version>1.3.2/<commons-io.version>/<code><code> <commons-net.version>3.3/<commons-net.version>/<code><code> <pagehelper.version>3.4.2/<pagehelper.version>/<code><code> <jsqlparser.version>0.9.1/<jsqlparser.version>/<code><code> <commons-fileupload.version>1.3.1/<commons-fileupload.version>/<code><code> <jedis.version>2.7.2/<jedis.version>/<code><code> <solrj.version>4.10.3/<solrj.version>/<code>
說明:和文件上傳有直接關係的是:
<code><dependency>/<code><code> <groupid>commons-fileupload/<groupid>/<code><code> <artifactid>commons-fileupload/<artifactid>/<code><code>
然後是 Web 項目的核心文件 web.xml
<code>/<code><code><web-app>www.w3.org/2001/XMLSchema-instance"/<web-app>/<code><code> xmlns="http://java.sun .com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"/<code><code> xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"/<code><code> id="taotao" version="2.5">/<code><code> <display-name>pictrue-service/<display-name>/<code><code> /<code><code> <context-param>/<code><code> <param-name>contextConfigLocation/<param-name>/<code><code> <param-value>classpath:spring/applicationContext-*.xml/<param-value>/<code><code> /<code><code> <listener>/<code><code> <listener-class>org.springframework.web.context.ContextLoaderListener/<listener-class>/<code><code> /<code><code> /<code><code> <filter>/<code><code> <filter-name>CharacterEncodingFilter/<filter-name>/<code><code> <filter-class>org.springframework.web.filter.CharacterEncodingFilter/<filter-class>/<code><code> <init-param>/<code><code> <param-name>encoding/<param-name>/<code><code> <param-value>utf-8/<param-value>/<code><code> /<code><code> /<code><code> <filter-mapping>/<code><code> <filter-name>CharacterEncodingFilter/<filter-name>/<code><code> <url-pattern>/*/<url-pattern>/<code><code> /<code><code> /<code><code> <servlet>/<code><code> <servlet-name>pictrue-service/<servlet-name>/<code><code> <servlet-class>org.springframework.web.servlet.DispatcherServlet/<servlet-class>/<code><code> <init-param>/<code><code> <param-name>contextConfigLocation/<param-name>/<code><code> <param-value>classpath:spring/springmvc.xml/<param-value>/<code><code> /<code><code> <load-on-startup>1/<load-on-startup>/<code><code> /<code><code> <servlet-mapping>/<code><code> <servlet-name>pictrue-service/<servlet-name>/<code><code> <url-pattern>//<url-pattern>/<code><code> /<code><code>
再是 SpringMVC 配置文件 springmvc.xml,需要添加文件上傳解析器
<code>/<code><code> <bean><code> class="org.springframework.web.multipart.commons.CommonsMultipartResolver">/<code><code> /<code><code> <property>/<code><code> /<code><code> <property>/<code><code> /<code>/<bean>/<code>
最後是 Ftp 配置文件 resource.properties
<code>FTP_ADDRESS=192.168.0.11/<code><code>FTP_PORT=21/<code><code>FTP_USERNAME=ftpuser/<code><code>FTP_PASSWORD=root/<code><code>FTP_BASE_PATH=/usr/local/nginx/html/images/<code><code>IMAGE_BASE_URL=http://192.168.0.11/images/<code>
Service 層
上傳圖片的接口 PictureService.java
<code>/**/<code><code> * 上傳,批量上傳接口/<code><code> * @param uploadFile/<code><code> * @return/<code><code> *//<code><code> Map uploadPicture(MultipartFile uploadFile);/<code><code>}/<code>
上傳圖片接口實現類 PictureServiceImpl.java
<code>@Service/<code><code>@SuppressWarnings({"rawtypes", "unchecked"})/<code><code>public class PictureServiceImpl implements PictureService {/<code>
<code> // 通過 Spring4 的 Value註解,獲取配置文件中的屬性值/<code><code> @Value("${FTP_ADDRESS}")/<code><code> private String FTP_ADDRESS; // ftp 服務器ip地址/<code><code> @Value("${FTP_PORT}")/<code><code> private Integer FTP_PORT; // ftp 服務器port,默認是21/<code><code> @Value("${FTP_USERNAME}")/<code><code> private String FTP_USERNAME; // ftp 服務器用戶名/<code><code> @Value("${FTP_PASSWORD}")/<code><code> private String FTP_PASSWORD; // ftp 服務器密碼/<code><code> @Value("${FTP_BASE_PATH}")/<code><code> private String FTP_BASE_PATH; // ftp 服務器存儲圖片的絕對路徑/<code><code> @Value("${IMAGE_BASE_URL}")/<code><code> private String IMAGE_BASE_URL; // ftp 服務器外網訪問圖片路徑/<code>
<code> @Override/<code><code> public Map uploadPicture(MultipartFile uploadFile) {/<code><code> Map resultMap = new HashMap<>;/<code><code> try {/<code><code> // 1. 取原始文件名/<code><code> String oldName = uploadFile.getOriginalFilename;/<code>
<code> // 2. ftp 服務器的文件名/<code><code> String newName = oldName;/<code><code> //圖片上傳/<code><code> boolean result = uploadFile(FTP_ADDRESS, FTP_PORT, FTP_USERNAME, FTP_PASSWORD, /<code><code> uploadFile.getInputStream, FTP_BASE_PATH, newName);/<code><code> //返回結果/<code><code> if(!result) {/<code><code> resultMap.put("error", 1);/<code><code> resultMap.put("message", "upload Fail");/<code><code> return resultMap;/<code><code> }/<code><code> resultMap.put("error", 0);/<code><code> resultMap.put("url", IMAGE_BASE_URL + "/" + newName);/<code><code> return resultMap;/<code>
<code> } catch (Exception e) {/<code><code> e.printStackTrace;/<code><code> resultMap.put("error", 1);/<code><code> resultMap.put("message", "upload Fail");/<code><code> return resultMap;/<code><code> }/<code><code> }/<code>
<code> /**/<code><code> * ftp 上傳圖片方法/<code><code> * @param ip ftp 服務器ip地址/<code><code> * @param port ftp 服務器port,默認是21/<code><code> * @param account ftp 服務器用戶名/<code><code> * @param passwd ftp 服務器密碼/<code><code> * @param inputStream 文件流/<code><code> * @param workingDir ftp 服務器存儲圖片的絕對路徑/<code><code> * @param fileName 上傳到ftp 服務器文件名/<code><code> * @throws Exception/<code><code> * /<code><code> *//<code><code> public boolean uploadFile(String ip, Integer port, String account, String passwd,/<code><code> InputStream inputStream, String workingDir, String fileName) throws Exception{/<code><code> boolean result = false;/<code><code> // 1. 創建一個FtpClient對象/<code><code> FTPClient ftpClient = new FTPClient;/<code><code> try {/<code><code> // 2. 創建 ftp 連接/<code><code> ftpClient.connect(ip, port);/<code><code> // 3. 登錄 ftp 服務器/<code><code> ftpClient.login(account, passwd);/<code><code> int reply = ftpClient.getReplyCode; // 獲取連接ftp 狀態返回值/<code><code> System.out.println("code : " + reply);/<code><code> if (!FTPReply.isPositiveCompletion(reply)) {/<code><code> ftpClient.disconnect; // 如果返回狀態不再 200 ~ 300 則認為連接失敗/<code><code> return result;/<code><code> }/<code><code> // 4. 讀取本地文件/<code><code>// FileInputStream inputStream = new FileInputStream(new File("F:\\\\hello.png"));/<code><code> // 5. 設置上傳的路徑/<code><code> ftpClient.changeWorkingDirectory(workingDir);/<code><code> // 6. 修改上傳文件的格式為二進制/<code><code> ftpClient.setFileType(FTP.BINARY_FILE_TYPE);/<code><code> // 7. 服務器存儲文件,第一個參數是存儲在服務器的文件名,第二個參數是文件流/<code><code> if (!ftpClient.storeFile(fileName, inputStream)) {/<code><code> return result;/<code><code> }/<code><code> // 8. 關閉連接/<code><code> inputStream.close;/<code><code> ftpClient.logout;/<code><code> result = true;/<code><code> } catch (Exception e) {/<code><code> e.printStackTrace;/<code><code> }finally {/<code><code> // FIXME 聽說,項目裡面最好少用try catch 捕獲異常,這樣會導致Spring的事務回滾出問題???難道之前寫的代碼都是假代碼!!!/<code><code> if (ftpClient.isConnected) {/<code><code> try {/<code><code> ftpClient.disconnect;/<code><code> } catch (IOException ioe) {/<code><code> }/<code><code> }/<code><code> }/<code><code> return result;/<code><code> }/<code><code>}/<code>
說明:
@Value 註解是Spring4 中提供的,@Value("${XXX}")
返回值是一個Map,並且key有error,url,message。這是根據KindEditer的語法要求來的。詳情見鏈接。http://kindeditor.net/docs/upload.html
Controller 層
負責頁面跳轉的 PageController.java
<code>@Controller/<code><code>public class PageController {/<code>
<code> /**/<code><code> * 打開首頁/<code><code> *//<code><code> @RequestMapping("/")/<code><code> public String showIndex {/<code><code> return "index";/<code><code> }/<code>
<code> @RequestMapping("/{page}")/<code><code> public String showpage(@PathVariable String page) {/<code><code> System.out.println("page : " + page);/<code><code> return page;/<code><code> }/<code><code>}/<code>
負責圖片上傳的 PictureController.java
<code>@RestController/<code><code>public class PictureController {/<code>
<code> @Autowired/<code><code> private PictureService pictureService;/<code>
<code> @RequestMapping("pic/upload")/<code><code> public String pictureUpload(@RequestParam(value = "fileUpload") MultipartFile uploadFile) {/<code><code> String json = "";/<code><code> try {/<code><code> Map result = pictureService.uploadPicture(uploadFile);/<code><code> // 瀏覽器擅長處理json格式的字符串,為了減少因為瀏覽器內核不同導致的bug,建議用json/<code><code> json = new ObjectMapper.writeValueAsString(result);/<code><code> } catch (JsonProcessingException e) {/<code><code> e.printStackTrace;/<code><code> }/<code><code> return json;/<code><code> }/<code><code>}/<code>
說明:
@RestController 也是Spring4 提供的,是 @Controller + @ResponseBody 的組合註解。
Controller層的返回值是一個json格式的字符串。是考慮到瀏覽器對json解析兼容性比較好。
Views視圖層
負責上傳圖片的視圖頁面:
<code><form>/<code><code> /<code><code> /<code><code> /<code><code>
/<code><code>借用KindEditor富文本編輯器實現批量上傳圖片
/<code><code> <textarea>dden;">/<textarea>/<code><code> /<code><code> $(function{/<code><code> //初始化富文本編輯器/<code><code> KindEditor.create("#kindEditorDesc", {/<code><code> // name值,必須和Controller 的參數對應,不然會提示 400 的錯誤/<code><code> filePostName : "fileUpload",/<code><code> // action值,/<code><code> uploadJson : '/pic/upload',/<code><code> // 設置上傳類型,分別為image、flash、media、file/<code><code> dir : "image"/<code><code> });/<code><code> });/<code><code> /<code>
說明:視圖分為兩個部分,第一個部分是為了測試上傳圖片功能的form表單。第二個部分是為了更好的體驗上傳,批量上傳,回顯功能的KindEditer 富文本編輯器。
總結
Nginx 搭建服務器的思維
Java實現 Ftp上傳圖片的功能
KindEditer 上傳圖片的功能
源碼:https://github.com/ITDragonBlog/daydayup/tree/master/Nginx
Nginx 搭建圖片服務器到這裡就結束了,有什麼不足的地方,請賜教。如果覺得不錯,可以點個贊哦!
如果喜歡本篇文章,歡迎轉發、點贊。關注訂閱號「Web項目聚集地」,回覆「全棧」即可獲取 2019 年最新 Java、Python、前端學習視頻資源。
閱讀更多 程序猿久一 的文章