在此之前如果你是一个Nginx新手可以先参阅:
本章知识点
效果图:
需求:实现图片的上传和批量上传
技术:Nginx,Vsftpd,Spring,SpringMVC,KindEditor,CentOS
说明:本章节内容主要是实现图片的上传功能。使用 KindEditer 是为了更好的演示图片的上传,回显,批量效果。后台代码与KindEditer没有直接关系,放心阅读。另外源码中有Mybatis的jar,不用理会,本章内容用不到,是为后续内容做准备!
源码:见文章底部
场景:用户将图片上传到 tomcat 服务器上,再由 tomcat 服务器通过FTP上传到 Nginx 服务器上。
项目结构:
单元测试
首先要攻破核心技术。通过单元测试实现图片上传的功能。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
package com.itdragon.test; import java.io.File; import java.io.FileInputStream; import org.apache.commons.net.ftp.FTP; import org.apache.commons.net.ftp.FTPClient; import org.junit.Test; public class PictureFTPTest { // 测试 ftp 上传图片功能 @Test public void testFtpClient() throws Exception { // 1. 创建一个FtpClient对象 FTPClient ftpClient = new FTPClient(); // 2. 创建 ftp 连接 ftpClient.connect("192.168.0.11", 21); // 3. 登录 ftp 服务器 ftpClient.login("ftpuser", "root"); // 4. 读取本地文件 FileInputStream inputStream = new FileInputStream(new File("F:\\hello.png")); // 5. 设置上传的路径 ftpClient.changeWorkingDirectory("/usr/local/nginx/html/images"); // 6. 修改上传文件的格式为二进制 ftpClient.setFileType(FTP.BINARY_FILE_TYPE); // 7. 服务器存储文件,第一个参数是存储在服务器的文件名,第二个参数是文件流 ftpClient.storeFile("hello.jpg", inputStream); // 8. 关闭连接 ftpClient.logout(); } } |
说明:这里的ip地址,端口,ftp用户名,密码,本地文件路径,以及Nginx服务器图片路径等,这些字符串参数都要根据自己实际设置的来填写的。如果你的Nginx和Vsftpd安装是按照我提供的链接来做的。那你只需要改ip地址即可。
Maven 的Web 项目
搭建Maven的Web 项目,之前有写过。这里就不过多描述。
项目核心配置文件
首先是 Maven 的核心文件 pom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
|
< project xmlns = "http://maven.apache.org/POM/4.0.0" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > < modelVersion >4.0.0</ modelVersion > < groupId >com.itdragon.upload</ groupId > < artifactId >pictrue-service</ artifactId > < version >0.0.1-SNAPSHOT</ version > < packaging >war</ packaging > <!-- 集中定义依赖版本号 --> < properties > < junit.version >4.12</ junit.version > < spring.version >4.1.3.RELEASE</ spring.version > < mybatis.version >3.2.8</ mybatis.version > < mybatis.spring.version >1.2.2</ mybatis.spring.version > < mybatis.paginator.version >1.2.15</ mybatis.paginator.version > < mysql.version >5.1.6</ mysql.version > < slf4j.version >1.6.4</ slf4j.version > < jackson.version >2.4.2</ jackson.version > < druid.version >1.0.9</ druid.version > < httpclient.version >4.3.5</ httpclient.version > < jstl.version >1.2</ jstl.version > < servlet-api.version >2.5</ servlet-api.version > < jsp-api.version >2.0</ jsp-api.version > < joda-time.version >2.5</ joda-time.version > < commons-lang3.version >3.3.2</ commons-lang3.version > < commons-io.version >1.3.2</ commons-io.version > < commons-net.version >3.3</ commons-net.version > < pagehelper.version >3.4.2</ pagehelper.version > < jsqlparser.version >0.9.1</ jsqlparser.version > < commons-fileupload.version >1.3.1</ commons-fileupload.version > < jedis.version >2.7.2</ jedis.version > < solrj.version >4.10.3</ solrj.version > </ properties > < dependencies > <!-- 时间操作组件 --> < dependency > < groupId >joda-time</ groupId > < artifactId >joda-time</ artifactId > < version >${joda-time.version}</ version > </ dependency > <!-- Apache工具组件 --> < dependency > < groupId >org.apache.commons</ groupId > < artifactId >commons-lang3</ artifactId > < version >${commons-lang3.version}</ version > </ dependency > < dependency > < groupId >org.apache.commons</ groupId > < artifactId >commons-io</ artifactId > < version >${commons-io.version}</ version > </ dependency > < dependency > < groupId >commons-net</ groupId > < artifactId >commons-net</ artifactId > < version >${commons-net.version}</ version > </ dependency > <!-- Jackson Json处理工具包 --> < dependency > < groupId >com.fasterxml.jackson.core</ groupId > < artifactId >jackson-databind</ artifactId > < version >${jackson.version}</ version > </ dependency > <!-- httpclient --> < dependency > < groupId >org.apache.httpcomponents</ groupId > < artifactId >httpclient</ artifactId > < version >${httpclient.version}</ version > </ dependency > <!-- 单元测试 --> < dependency > < groupId >junit</ groupId > < artifactId >junit</ artifactId > < version >${junit.version}</ version > < scope >test</ scope > </ dependency > <!-- 日志处理 --> < dependency > < groupId >org.slf4j</ groupId > < artifactId >slf4j-log4j12</ artifactId > < version >${slf4j.version}</ version > </ dependency > <!-- Mybatis --> < dependency > < groupId >org.mybatis</ groupId > < artifactId >mybatis</ artifactId > < version >${mybatis.version}</ version > </ dependency > < dependency > < groupId >org.mybatis</ groupId > < artifactId >mybatis-spring</ artifactId > < version >${mybatis.spring.version}</ version > </ dependency > < dependency > < groupId >com.github.miemiedev</ groupId > < artifactId >mybatis-paginator</ artifactId > < version >${mybatis.paginator.version}</ version > </ dependency > < dependency > < groupId >com.github.pagehelper</ groupId > < artifactId >pagehelper</ artifactId > < version >${pagehelper.version}</ version > </ dependency > <!-- MySql --> < dependency > < groupId >mysql</ groupId > < artifactId >mysql-connector-java</ artifactId > < version >${mysql.version}</ version > </ dependency > <!-- 连接池 --> < dependency > < groupId >com.alibaba</ groupId > < artifactId >druid</ artifactId > < version >${druid.version}</ version > </ dependency > <!-- Spring --> < dependency > < groupId >org.springframework</ groupId > < artifactId >spring-context</ artifactId > < version >${spring.version}</ version > </ dependency > < dependency > < groupId >org.springframework</ groupId > < artifactId >spring-beans</ artifactId > < version >${spring.version}</ version > </ dependency > < dependency > < groupId >org.springframework</ groupId > < artifactId >spring-webmvc</ artifactId > < version >${spring.version}</ version > </ dependency > < dependency > < groupId >org.springframework</ groupId > < artifactId >spring-jdbc</ artifactId > < version >${spring.version}</ version > </ dependency > < dependency > < groupId >org.springframework</ groupId > < artifactId >spring-aspects</ artifactId > < version >${spring.version}</ version > </ dependency > <!-- JSP相关 --> < dependency > < groupId >jstl</ groupId > < artifactId >jstl</ artifactId > < version >${jstl.version}</ version > </ dependency > < dependency > < groupId >javax.servlet</ groupId > < artifactId >servlet-api</ artifactId > < version >${servlet-api.version}</ version > < scope >provided</ scope > </ dependency > < dependency > < groupId >javax.servlet</ groupId > < artifactId >jsp-api</ artifactId > < version >${jsp-api.version}</ version > < scope >provided</ scope > </ dependency > <!-- 文件上传组件 --> < dependency > < groupId >commons-fileupload</ groupId > < artifactId >commons-fileupload</ artifactId > < version >${commons-fileupload.version}</ version > </ dependency > <!-- Redis客户端 --> < dependency > < groupId >redis.clients</ groupId > < artifactId >jedis</ artifactId > < version >${jedis.version}</ version > </ dependency > <!-- solr客户端 --> < dependency > < groupId >org.apache.solr</ groupId > < artifactId >solr-solrj</ artifactId > < version >${solrj.version}</ version > </ dependency > </ dependencies > < build > < finalName >${project.artifactId}</ finalName > < plugins > <!-- 资源文件拷贝插件 --> < plugin > < groupId >org.apache.maven.plugins</ groupId > < artifactId >maven-resources-plugin</ artifactId > < version >2.7</ version > < configuration > < encoding >UTF-8</ encoding > </ configuration > </ plugin > <!-- java编译插件 --> < plugin > < groupId >org.apache.maven.plugins</ groupId > < artifactId >maven-compiler-plugin</ artifactId > < version >3.2</ version > < configuration > < source >1.7</ source > < target >1.7</ target > < encoding >UTF-8</ encoding > </ configuration > </ plugin > </ plugins > < pluginManagement > < plugins > <!-- 配置Tomcat插件 --> < plugin > < groupId >org.apache.tomcat.maven</ groupId > < artifactId >tomcat7-maven-plugin</ artifactId > < version >2.2</ version > </ plugin > </ plugins > </ pluginManagement > </ build > </ project > |
说明:和文件上传有直接关系的是:
1
2
3
4
|
< dependency > < groupId >commons-fileupload</ groupId > < artifactId >commons-fileupload</ artifactId > </ dependency > |
然后是 Web 项目的核心文件 web.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
<? xml version = "1.0" encoding = "UTF-8" ?> < web-app xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xmlns = "http://java.sun.com/xml/ns/javaee" xmlns:web = "http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation = "http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id = "taotao" version = "2.5" > < display-name >pictrue-service</ display-name > <!-- 加载spring容器 --> < context-param > < param-name >contextConfigLocation</ param-name > < param-value >classpath:spring/applicationContext-*.xml</ param-value > </ context-param > < listener > < listener-class >org.springframework.web.context.ContextLoaderListener</ listener-class > </ listener > <!-- 解决post乱码 --> < filter > < filter-name >CharacterEncodingFilter</ filter-name > < filter-class >org.springframework.web.filter.CharacterEncodingFilter</ filter-class > < init-param > < param-name >encoding</ param-name > < param-value >utf-8</ param-value > </ init-param > </ filter > < filter-mapping > < filter-name >CharacterEncodingFilter</ filter-name > < url-pattern >/*</ url-pattern > </ filter-mapping > <!-- springmvc的前端控制器 --> < servlet > < servlet-name >pictrue-service</ servlet-name > < servlet-class >org.springframework.web.servlet.DispatcherServlet</ servlet-class > < init-param > < param-name >contextConfigLocation</ param-name > < param-value >classpath:spring/springmvc.xml</ param-value > </ init-param > < load-on-startup >1</ load-on-startup > </ servlet > < servlet-mapping > < servlet-name >pictrue-service</ servlet-name > < url-pattern >/</ url-pattern > </ servlet-mapping > </ web-app > |
再是 SpringMVC 配置文件 springmvc.xml,需要添加文件上传解析器
1
2
3
4
5
6
7
8
|
<!-- 定义文件上传解析器 --> < bean id = "multipartResolver" class = "org.springframework.web.multipart.commons.CommonsMultipartResolver" > <!-- 设定默认编码 --> < property name = "defaultEncoding" value = "UTF-8" ></ property > <!-- 设定文件上传的最大值5MB,5*1024*1024 --> < property name = "maxUploadSize" value = "5242880" ></ property > </ bean > |
最后是 Ftp 配置文件 resource.properties
1
2
3
4
5
6
|
FTP_ADDRESS=192.168.0.11 FTP_PORT=21 FTP_USERNAME=ftpuser FTP_PASSWORD=root FTP_BASE_PATH=/usr/local/nginx/html/images IMAGE_BASE_URL=http://192.168.0.11/images |
Service 层
上传图片的接口 PictureService.java
1
2
3
4
5
6
7
8
9
10
11
12
|
package com.itdragon.service; import java.util.Map; import org.springframework.web.multipart.MultipartFile; public interface PictureService { /** * 上传,批量上传接口 * @param uploadFile * @return */ Map uploadPicture(MultipartFile uploadFile); } |
上传图片接口实现类 PictureServiceImpl.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
package com.itdragon.service.impl; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import org.apache.commons.net.ftp.FTP; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPReply; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import com.itdragon.service.PictureService; @Service @SuppressWarnings({"rawtypes", "unchecked"}) public class PictureServiceImpl implements PictureService { // 通过 Spring4 的 Value注解,获取配置文件中的属性值 @Value("${FTP_ADDRESS}") private String FTP_ADDRESS; // ftp 服务器ip地址 @Value("${FTP_PORT}") private Integer FTP_PORT; // ftp 服务器port,默认是21 @Value("${FTP_USERNAME}") private String FTP_USERNAME; // ftp 服务器用户名 @Value("${FTP_PASSWORD}") private String FTP_PASSWORD; // ftp 服务器密码 @Value("${FTP_BASE_PATH}") private String FTP_BASE_PATH; // ftp 服务器存储图片的绝对路径 @Value("${IMAGE_BASE_URL}") private String IMAGE_BASE_URL; // ftp 服务器外网访问图片路径 @Override public Map uploadPicture(MultipartFile uploadFile) { Map resultMap = new HashMap<>(); try { // 1. 取原始文件名 String oldName = uploadFile.getOriginalFilename(); // 2. ftp 服务器的文件名 String newName = oldName; //图片上传 boolean result = uploadFile(FTP_ADDRESS, FTP_PORT, FTP_USERNAME, FTP_PASSWORD, uploadFile.getInputStream(), FTP_BASE_PATH, newName); //返回结果 if(!result) { resultMap.put("error", 1); resultMap.put("message", "upload Fail"); return resultMap; } resultMap.put("error", 0); resultMap.put("url", IMAGE_BASE_URL + "/" + newName); return resultMap; } catch (Exception e) { e.printStackTrace(); resultMap.put("error", 1); resultMap.put("message", "upload Fail"); return resultMap; } } /** * ftp 上传图片方法 * @param ip ftp 服务器ip地址 * @param port ftp 服务器port,默认是21 * @param account ftp 服务器用户名 * @param passwd ftp 服务器密码 * @param inputStream 文件流 * @param workingDir ftp 服务器存储图片的绝对路径 * @param fileName 上传到ftp 服务器文件名 * @throws Exception * */ public boolean uploadFile(String ip, Integer port, String account, String passwd, InputStream inputStream, String workingDir, String fileName) throws Exception{ boolean result = false; // 1. 创建一个FtpClient对象 FTPClient ftpClient = new FTPClient(); try { // 2. 创建 ftp 连接 ftpClient.connect(ip, port); // 3. 登录 ftp 服务器 ftpClient.login(account, passwd); int reply = ftpClient.getReplyCode(); // 获取连接ftp 状态返回值 System.out.println("code : " + reply); if (!FTPReply.isPositiveCompletion(reply)) { ftpClient.disconnect(); // 如果返回状态不再 200 ~ 300 则认为连接失败 return result; } // 4. 读取本地文件 // FileInputStream inputStream = new FileInputStream(new File("F:\\hello.png")); // 5. 设置上传的路径 ftpClient.changeWorkingDirectory(workingDir); // 6. 修改上传文件的格式为二进制 ftpClient.setFileType(FTP.BINARY_FILE_TYPE); // 7. 服务器存储文件,第一个参数是存储在服务器的文件名,第二个参数是文件流 if (!ftpClient.storeFile(fileName, inputStream)) { return result; } // 8. 关闭连接 inputStream.close(); ftpClient.logout(); result = true; } catch (Exception e) { e.printStackTrace(); }finally { // FIXME 听说,项目里面最好少用try catch 捕获异常,这样会导致Spring的事务回滚出问题???难道之前写的代码都是假代码!!! if (ftpClient.isConnected()) { try { ftpClient.disconnect(); } catch (IOException ioe) { } } } return result; } } |
说明:
① @Value 注解是Spring4 中提供的,@Value("${XXX}")
② 返回值是一个Map,并且key有error,url,message。这是根据KindEditer的语法要求来的。
Controller 层
负责页面跳转的 PageController.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
package com.itdragon.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class PageController { /** * 打开首页 */ @RequestMapping("/") public String showIndex() { return "index"; } @RequestMapping("/{page}") public String showpage(@PathVariable String page) { System.out.println("page : " + page); return page; } } |
负责图片上传的 PictureController.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
package com.itdragon.controller; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.itdragon.service.PictureService; @RestController public class PictureController { @Autowired private PictureService pictureService; @RequestMapping("pic/upload") public String pictureUpload(@RequestParam(value = "fileUpload") MultipartFile uploadFile) { String json = ""; try { Map result = pictureService.uploadPicture(uploadFile); // 浏览器擅长处理json格式的字符串,为了减少因为浏览器内核不同导致的bug,建议用json json = new ObjectMapper().writeValueAsString(result); } catch (JsonProcessingException e) { e.printStackTrace(); } return json; } } |
说明:
① @RestController 也是Spring4 提供的,是 @Controller + @ResponseBody 的组合注解。
② Controller层的返回值是一个json格式的字符串。是考虑到浏览器对json解析兼容性比较好。
Views视图层
负责上传图片的jsp页面 pic-upload.jsp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
<%@ page language="java" contentType="text/html; UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> < html > < head > < meta http-equiv = "Content-Type" content = "text/html; charset=UTF-8" > < title >ITDragon 图片上传</ title > </ head > < link href = "/js/kindeditor-4.1.10/themes/default/default.css" rel = "external nofollow" type = "text/css" rel = "stylesheet" > < script type = "text/javascript" src = "js/jquery.min.js" ></ script > < script type = "text/javascript" charset = "utf-8" src = "/js/kindeditor-4.1.10/kindeditor-all-min.js" ></ script > < script type = "text/javascript" charset = "utf-8" src = "/js/kindeditor-4.1.10/lang/zh_CN.js" ></ script > </ head > < body > < h3 >测试上传图片功能接口的form表单</ h3 > < form action = "pic/upload" method = "post" enctype = "multipart/form-data" > < input type = "file" name = "fileUpload" /> < input type = "submit" value = "上传文件" /> </ form > < hr /> < h3 >借用KindEditor富文本编辑器实现批量上传图片</ h3 > < textarea id = "kindEditorDesc" style = "width:800px;height:300px;visibility:hidden;" ></ textarea > < script type = "text/javascript" > $(function(){ //初始化富文本编辑器 KindEditor.create("#kindEditorDesc", { // name值,必须和Controller 的参数对应,不然会提示 400 的错误 filePostName : "fileUpload", // action值, uploadJson : '/pic/upload', // 设置上传类型,分别为image、flash、media、file dir : "image" }); }); </ script > </ body > </ html > |
说明:pic-upload.jsp 分为两个部分,第一个部分是为了测试上传图片功能的form表单。第二个部分是为了更好的体验上传,批量上传,回显功能的KindEditer 富文本编辑器。
总结 Nginx 搭建服务器的思维 Java实现 Ftp上传图片的功能 KindEditer 上传图片的功能
原文链接:http://www.cnblogs.com/itdragon/p/7864916.html