扫码关注公众号:芋道源码

发送: 百事可乐
获取永久解锁本站全部文章的链接

《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 http://cnblogs.com/itdragon/p/7864916.html 「ITDragon龙」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

本章内容通过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 服务器上。

项目结构:


单元测试

首先要攻破核心技术。通过单元测试实现图片上传的功能。

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

<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>

说明:和文件上传有直接关系的是:

<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
</dependency>

然后是 Web 项目的核心文件 web.xml

<?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,需要添加文件上传解析器

<!-- 定义文件上传解析器 -->
<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

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

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

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的语法要求来的。详情见链接。http://kindeditor.net/docs/upload.html

Controller 层

负责页面跳转的 PageController.java

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

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

<%@ 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" 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 上传图片的功能

源码:https://github.com/ITDragonBlog/daydayup/tree/master/Nginx

Nginx 搭建图片服务器到这里就结束了,有什么不足的地方,请赐教。如果觉得不错,可以点个赞哦!

文章目录
  1. 1. 本章知识点
  2. 2. 单元测试
  3. 3. Maven 的Web 项目
    1. 3.1. 项目核心配置文件
    2. 3.2. Service 层
    3. 3.3. Controller 层
    4. 3.4. Views视图层
  4. 4. 总结