[第三天传智播客淘淘商城之图片上传, 搭建nginx图片服务器/ftp服务器]
图片上传
传统项目中的图片管理
传统项目中,可以在web项目中添加一个文件夹,来存放上传的图片。例如在工程的根目录WebRoot下创建一个images文件夹。把图片存放在此文件夹中就可以直接使用在工程中引用。
优点:引用方便,便于管理
缺点:
1、如果是分布式环境图片引用会出现问题。
2、图片的下载会给服务器增加额外的压力
分布式环境的图片管理
分布式环境一般都有一个专门的图片服务器存放图片。
我们使用虚拟机搭建一个专门的服务器来存放图片。在此服务器上安装一个nginx来提供http服务,安装一个ftp服务器来提供图片上传服务。
搭建图片服务器
第一步:安装vsftpd提供ftp服务
详见:http://www.cnblogs.com/josephcnblog/articles/6930572.html
第二步:安装nginx提供http服务
详见:http://www.cnblogs.com/josephcnblog/articles/6930593.html
测试图片服务器
ftp服务测试
a)使用ftp客户端
b)使用java程序
ftp可以需要依赖commons-net-3.3.jar包。
public static void main(String[] args) throws Exception { FTPClient ftpClient = new FTPClient(); ftpClient.connect("192.168.25.200"); ftpClient.login("ftpuser", "ftpuser"); FileInputStream inputStream = new FileInputStream(new File("D:\\Documents\\Pictures\\pics\\21.jpg")); ftpClient.setFileType(FTP.BINARY_FILE_TYPE); ftpClient.storeFile("123.jpg", inputStream); inputStream.close(); ftpClient.logout(); }
1. http服务测试
a) 浏览器测试
SpringMVC中实现图片上传
上传思路:
第一步:
导入common-fileupload的依赖
<!-- 文件上传组件 --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> </dependency>
第二步:
在SpringMVC配置文件中添加文件上传解析器
<!-- 定义文件上传解析器 --> <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>
Service实现
1. 获取资源配置文件的内容
第一步:
创建资源配置文件
FILI_UPLOAD_PATH=D:/temp/imagestest/webapps/images IMAGE_BASE_URL=http://localhost:9000/images
第二步:
在Spring(taotao-manage-servlet.xml)容器中加载资源文件
第二步:
在Service中获取资源配置:
@Value("${FILI_UPLOAD_PATH}") private String FILI_UPLOAD_PATH; @Value("${IMAGE_BASE_URL}") private String IMAGE_BASE_URL;
图片名生成策略
时间+随机数
/** * 图片名生成 */ public static String genImageName() { //取当前时间的长整形值包含毫秒 long millis = System.currentTimeMillis(); //long millis = System.nanoTime(); //加上三位随机数 Random random = new Random(); int end3 = random.nextInt(999); //如果不足三位前面补0 String str = millis + String.format("%03d", end3); return str; }
使用UUID:
UUID.randomUUID();
1. Service实现
@Service public class PictureServiceImpl implements PictureService { @Value("${IMAGE_BASE_URL}") private String IMAGE_BASE_URL; @Value("${FILI_UPLOAD_PATH}") private String FILI_UPLOAD_PATH; @Value("${FTP_SERVER_IP}") private String FTP_SERVER_IP; @Value("${FTP_SERVER_PORT}") private Integer FTP_SERVER_PORT; @Value("${FTP_SERVER_USERNAME}") private String FTP_SERVER_USERNAME; @Value("${FTP_SERVER_PASSWORD}") private String FTP_SERVER_PASSWORD; @Override public PictureResult uploadFile(MultipartFile uploadFile) throws Exception { // 上传文件功能实现 String path = savePicture(uploadFile); // 回显 PictureResult result = new PictureResult(0, IMAGE_BASE_URL + path); return result; } private String savePicture(MultipartFile uploadFile) { String result = null; try { // 上传文件功能实现 // 判断文件是否为空 if (uploadFile.isEmpty()) return null; // 上传文件以日期为单位分开存放,可以提高图片的查询速度 String filePath = "/" + new SimpleDateFormat("yyyy").format(new Date()) + "/" + new SimpleDateFormat("MM").format(new Date()) + "/" + new SimpleDateFormat("dd").format(new Date()); // 取原始文件名 String originalFilename = uploadFile.getOriginalFilename(); // 新文件名 String newFileName = IDUtils.genImageName() + originalFilename.substring(originalFilename.lastIndexOf(".")); // 转存文件,上传到ftp服务器 FtpUtil.uploadFile(FTP_SERVER_IP, FTP_SERVER_PORT, FTP_SERVER_USERNAME, FTP_SERVER_PASSWORD, FILI_UPLOAD_PATH, filePath, newFileName, uploadFile.getInputStream()); result = filePath + "/" + newFileName; } catch (Exception e) { e.printStackTrace(); } return result; } }
Controller实现
@Controller @RequestMapping("/pic") public class PictureController { @Autowired private PictureService pictureService; @RequestMapping("/upload") @ResponseBody public PictureResult uploda(MultipartFile uploadFile) throws Exception { //调用service上传图片 PictureResult pictureResult = pictureService.uploadFile(uploadFile); //返回上传结果 return pictureResult; } }
前端JS实现图片上传
Js实现逻辑
=====================================
工具类:
IDUtils.java
package com.taotao.utils; import java.util.Random; /** * 各种id生成策略 * <p>Title: IDUtils</p> * <p>Description: </p> * <p>Company: www.itcast.com</p> * @author 入云龙 * @date 2015年7月22日下午2:32:10 * @version 1.0 */ public class IDUtils { /** * 图片名生成 */ public static String genImageName() { //取当前时间的长整形值包含毫秒 long millis = System.currentTimeMillis(); //long millis = System.nanoTime(); //加上三位随机数 Random random = new Random(); int end3 = random.nextInt(999); //如果不足三位前面补0 String str = millis + String.format("%03d", end3); return str; } /** * 商品id生成 */ public static long genItemId() { //取当前时间的长整形值包含毫秒 long millis = System.currentTimeMillis(); //long millis = System.nanoTime(); //加上两位随机数 Random random = new Random(); int end2 = random.nextInt(99); //如果不足两位前面补0 String str = millis + String.format("%02d", end2); long id = new Long(str); return id; } public static void main(String[] args) { for(int i=0;i< 100;i++) System.out.println(genItemId()); } }
FtpUtil.java
package com.taotao.utils; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.apache.commons.net.ftp.FTP; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPFile; import org.apache.commons.net.ftp.FTPReply; /** * ftp上传下载工具类 * <p>Title: FtpUtil</p> * <p>Description: </p> * <p>Company: www.itcast.com</p> * @author 入云龙 * @date 2015年7月29日下午8:11:51 * @version 1.0 */ public class FtpUtil { /** * Description: 向FTP服务器上传文件 * @param host FTP服务器hostname * @param port FTP服务器端口 * @param username FTP登录账号 * @param password FTP登录密码 * @param basePath FTP服务器基础目录 * @param filePath FTP服务器文件存放路径。例如分日期存放:/2015/01/01。文件的路径为basePath+filePath * @param filename 上传到FTP服务器上的文件名 * @param input 输入流 * @return 成功返回true,否则返回false */ public static boolean uploadFile(String host, int port, String username, String password, String basePath, String filePath, String filename, InputStream input) { boolean result = false; FTPClient ftp = new FTPClient(); try { int reply; ftp.connect(host, port);// 连接FTP服务器 // 如果采用默认端口,可以使用ftp.connect(host)的方式直接连接FTP服务器 ftp.login(username, password);// 登录 reply = ftp.getReplyCode(); if (!FTPReply.isPositiveCompletion(reply)) { ftp.disconnect(); return result; } //切换到上传目录 if (!ftp.changeWorkingDirectory(basePath+filePath)) { //如果目录不存在创建目录 String[] dirs = filePath.split("/"); String tempPath = basePath; for (String dir : dirs) { if (null == dir || "".equals(dir)) continue; tempPath += "/" + dir; if (!ftp.changeWorkingDirectory(tempPath)) { if (!ftp.makeDirectory(tempPath)) { return result; } else { ftp.changeWorkingDirectory(tempPath); } } } } //设置上传文件的类型为二进制类型 ftp.setFileType(FTP.BINARY_FILE_TYPE); //上传文件 if (!ftp.storeFile(filename, input)) { return result; } input.close(); ftp.logout(); result = true; } catch (IOException e) { e.printStackTrace(); } finally { if (ftp.isConnected()) { try { ftp.disconnect(); } catch (IOException ioe) { } } } return result; } /** * Description: 从FTP服务器下载文件 * @param host FTP服务器hostname * @param port FTP服务器端口 * @param username FTP登录账号 * @param password FTP登录密码 * @param remotePath FTP服务器上的相对路径 * @param fileName 要下载的文件名 * @param localPath 下载后保存到本地的路径 * @return */ public static boolean downloadFile(String host, int port, String username, String password, String remotePath, String fileName, String localPath) { boolean result = false; FTPClient ftp = new FTPClient(); try { int reply; ftp.connect(host, port); // 如果采用默认端口,可以使用ftp.connect(host)的方式直接连接FTP服务器 ftp.login(username, password);// 登录 reply = ftp.getReplyCode(); if (!FTPReply.isPositiveCompletion(reply)) { ftp.disconnect(); return result; } ftp.changeWorkingDirectory(remotePath);// 转移到FTP服务器目录 FTPFile[] fs = ftp.listFiles(); for (FTPFile ff : fs) { if (ff.getName().equals(fileName)) { File localFile = new File(localPath + "/" + ff.getName()); OutputStream is = new FileOutputStream(localFile); ftp.retrieveFile(ff.getName(), is); is.close(); } } ftp.logout(); result = true; } catch (IOException e) { e.printStackTrace(); } finally { if (ftp.isConnected()) { try { ftp.disconnect(); } catch (IOException ioe) { } } } return result; } public static void main(String[] args) { try { FileInputStream in=new FileInputStream(new File("D:\\temp\\image\\gaigeming.jpg")); boolean flag = uploadFile("192.168.25.133", 21, "ftpuser", "ftpuser", "/home/ftpuser/www/images","/2015/01/21", "gaigeming.jpg", in); System.out.println(flag); } catch (FileNotFoundException e) { e.printStackTrace(); } } }
JsonUtils.java【这个工具的作用是:返回json格式数据时,chrome浏览器可以兼容,但是火狐浏览器不兼容,为了所有浏览器都兼容,需要返回字符串】
package com.taotao.utils; import java.util.List; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.taotao.result.TaotaoResult; /** * 淘淘商城自定义响应结构 */ public class JsonUtils { // 定义jackson对象 private static final ObjectMapper MAPPER = new ObjectMapper(); /** * 将对象转换成json字符串。 * <p>Title: pojoToJson</p> * <p>Description: </p> * @param data * @return */ public static String objectToJson(Object data) { try { String string = MAPPER.writeValueAsString(data); return string; } catch (JsonProcessingException e) { e.printStackTrace(); } return null; } /** * 将json结果集转化为对象 * * @param jsonData json数据 * @param clazz 对象中的object类型 * @return */ public static <T> T jsonToPojo(String jsonData, Class<T> beanType) { try { T t = MAPPER.readValue(jsonData, beanType); return t; } catch (Exception e) { e.printStackTrace(); } return null; } /** * 将json数据转换成pojo对象list * <p>Title: jsonToList</p> * <p>Description: </p> * @param jsonData * @param beanType * @return */ public static <T>List<T> jsonToList(String jsonData, Class<T> beanType) { JavaType javaType = MAPPER.getTypeFactory().constructParametricType(List.class, beanType); try { List<T> list = MAPPER.readValue(jsonData, javaType); return list; } catch (Exception e) { e.printStackTrace(); } return null; } }
PictureResult.java
package com.taotao.result; /** * 上传图片返回值 * <p>Title: PictureResult</p> * <p>Description: </p> * <p>Company: www.itcast.com</p> * @author 入云龙 * @date 2015年7月22日下午2:09:02 * @version 1.0 */ public class PictureResult { /** * 上传图片返回值,成功:0 失败:1 */ private Integer error; /** * 回显图片使用的url */ private String url; /** * 错误时的错误消息 */ private String message; public PictureResult(Integer state, String url) { this.url = url; this.error = state; } public PictureResult(Integer state, String url, String errorMessage) { this.url = url; this.error = state; this.message = errorMessage; } public Integer getError() { return error; } public void setError(Integer error) { this.error = error; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }