淘淘商城---8.9

Posted 汪本成

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了淘淘商城---8.9相关的知识,希望对你有一定的参考价值。

昨天忘记给大家说了个事,昨天添加FTP服务器依赖那部分我在搭建项目开始时就已经在taotao-common的pom文件下写好了,大家可以回去看看里面添加Apache组件那部分里面就有。

1、nginx+FTP出现403错误

还有就是访问nginx下的ftp图片会有我遇到的这个问题,如图:


我不知道大家有没有遇到,假如遇到也不知道大家是怎么去解决的,我把我的解决可以具体点写出来。

1.1、错误分析:

我看了下网上的关于这方面的相关资料,大概总结的两个可能的原因:

1、缺少index.html或者index.php文件(索引文件)

2、权限问题

这是我nginx修改后的配置文件

因此我现在直接排除掉第一种情况。

怎么说呢。果然大部分原因还是会出现在第二种情况下,这就要我们需要去熟悉linux了,可怜的小伙伴们不知道学习的咋样。先解决问题在讨论学习方面吧。

1.2、错误解决

一般这种情况我的是因为是在root用户下编译的安装及启动nginx的,会出现权限问题,没跟ftp服务器所属用户一致导致。于是我就改变了nginx所属的用户和用户组。

root@cdh4>chmod 777 /home/ftpuser/www
root@cdh4>chown -R ftpuser:ftpuser /usr/local/nginx
root@cdh4>/usr/local/nginx/sbin/nginx -s reload
之后在打开浏览器就可以访问的到我昨天用测试代码上传的图片了,地址: http://blog.csdn.net/sinat_31726559/article/details/52153330

如果还是不行就重启一下机器,在关闭好iptables或者firewall就可以了

2、访问Nginx图片失真

上面虽然我们能够访问到图片了,但是图片却存在失真的情况,这又是怎么一回事呢?

2.1、错误分析

先看看昨天我写的测试代码


从上面可以看出我上传到ftp服务器是以字节流传输的,到服务器后是文本格式,而图片是二进制格式,所以上传上去或出现编码不能恢复到原来的图片模样。

2.2、错误解决

知道错误原因就好解决了,只要修改上传文件的格式就行了,添加以下一句代码就ok了

//修改上传文件格式
client.setFileType(FTP.BINARY_FILE_TYPE);

将之前上传的图片先从服务器删除,在用java代码上传一回


上传ok,接着我们再刷新一下浏览器看看效果。

呵呵,搞定!

3、项目所用到的FTP工具类

这里因为是项目中所用到的工具类。考虑到代码的复用性,我就在taotao-common下新建的一个utils的工具类的包,如下:

工具类代码如下:

代码一

package com.taotao.common.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;

/**
 * 
  * @ClassName: FtpUtil  
  * @Description: TODO(ftp服务器的工具类)  
  * @author 汪本成  
  * @date 2016年8月9日 上午10:43:38  
  *
 */
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();  
	      
	


这个工具类的测试代码就让你们写写了,不会可以给我留言,及时给你答复

4、图片上传的实现

4.1、需求分析

Common.js

1、绑定事件,上传图片的组件

2、初始化参数

2、上传图片的url:

/pic/upload

3、上图片参数名称:

uploadFile

4、返回结果数据类型json

参考文档:http://kindeditor.net/docs/upload.html

返回格式(JSON)

//成功时

        "error" : 0,
        "url" : "http://www.example.com/path/to/file.ext"

//失败时

        "error" : 1,
        "message" : "错误信息"

5、整个组建关键代码

代码二

var TT = TAOTAO = 
	// 编辑器参数
	kingEditorParams : 
		//指定上传文件参数名称
		filePostName  : "uploadFile",
		//指定上传文件请求的url。
		uploadJson : '/pic/upload',
		//上传类型,分别为image、flash、media、file
		dir : "image"
	,
	// 格式化时间
	formatDateTime : function(val,row)
		var now = new Date(val);
    	return now.format("yyyy-MM-dd hh:mm:ss");
	,
	// 格式化连接
	formatUrl : function(val,row)
		if(val)
			return "<a href='"+val+"' target='_blank'>查看</a>";			
		
		return "";
	,
	// 格式化价格
	formatPrice : function(val,row)
		return (val/1000).toFixed(2);
	,
	// 格式化商品的状态
	formatItemStatus : function formatStatus(val,row)
        if (val == 1)
            return '正常';
         else if(val == 2)
        	return '<span style="color:red;">下架</span>';
         else 
        	return '未知';
        
    ,
    
    init : function(data)
    	// 初始化图片上传组件
    	this.initPicUpload(data);
    	// 初始化选择类目组件
    	this.initItemCat(data);
    ,
    // 初始化图片上传组件
    initPicUpload : function(data)
    	$(".picFileUpload").each(function(i,e)
    		var _ele = $(e);
    		_ele.siblings("div.pics").remove();
    		_ele.after('\\
    			<div class="pics">\\
        			<ul></ul>\\
        		</div>');
    		// 回显图片
        	if(data && data.pics)
        		var imgs = data.pics.split(",");
        		for(var i in imgs)
        			if($.trim(imgs[i]).length > 0)
        				_ele.siblings(".pics").find("ul").append("<li><a href='"+imgs[i]+"' target='_blank'><img src='"+imgs[i]+"' width='80' height='50' /></a></li>");
        			
        		
        	
        	//给“上传图片按钮”绑定click事件
        	$(e).click(function()
        		var form = $(this).parentsUntil("form").parent("form");
        		//打开图片上传窗口
        		KindEditor.editor(TT.kingEditorParams).loadPlugin('multiimage',function()
        			var editor = this;
        			editor.plugin.multiImageDialog(
						clickFn : function(urlList) 
							var imgArray = [];
							KindEditor.each(urlList, function(i, data) 
								imgArray.push(data.url);
								form.find(".pics ul").append("<li><a href='"+data.url+"' target='_blank'><img src='"+data.url+"' width='80' height='50' /></a></li>");
							);
							form.find("[name=image]").val(imgArray.join(","));
							editor.hideDialog();
						
					);
        		);
        	);
    	);
    ,
详细的记得要查看我上面发给你的链接资料哟。

4.2、Service

功能:接收controller层传递过来的图片对象,把图片上传到ftp服务器。给图片生成一个新的名字,防止文件名重复。返回文件的url路径。需要保证图片上传插件的数据方式。


这里有两种实现方式:

1、创建一个pojo对象来实现

2、创建一个Map实现

这里我用Map实现。

Map中的内容:

key                         Value                
Error1、0
URL图片的url(成功时)                          
Message错误信息(失败时)

首先去service里面定义一个接口,为PictureService


代码三

package com.taotao.service;

import java.util.Map;

import org.springframework.web.multipart.MultipartFile;

/**
 * 
  * @ClassName: PictureService  
  * @Description: TODO(图片上传接口)  
  * @author 汪本成  
  * @date 2016年8月9日 下午12:01:27  
  *
 */
public interface PictureService 
	
	Map<?, ?> uploadFile(MultipartFile uploadFile);



对接口进行实现,但是实现时候我们得先整理好我们的思路。

1、对生成的文件名要保证能够不进行重复,开始我想到的是UUID,但是感觉太长了。就用一个生成id的工具类解决,代码如下:

代码四

package com.taotao.common.utils;

import java.util.Random;

/**
 * 
  * @ClassName: IDUtils  
  * @Description: TODO(各种id生成策略)  
  * @author 汪本成  
  * @date 2016年8月9日 下午12:40:19  
  *
 */
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());
	

2、进行图片上传时,我们首先考虑到不能绑定死一个机器,在代码中就决定或者说写死这个机器信息,应该在配置文件里进行配置,在读取配置文件信息会比较好,于是新建一个properties文件来记录信息。这里我们绝对不能将这个配置文件写到jar包工程下,所以在taotao-manager-web工程下的resource文件夹下新建一个resource.properties文件来保存配置信息,如下:

代码五

#FTP相关配置
#FTP ip地址
FTP_IP=192.168.43.163
FTP_PORT=21
FTP_USERNAME=ftpuser
FTP_PASSWORD=115010
FTP_BASE_PATH=/home/ftpuser/www/images
#图片服务器的相关配置
#图片服务器的基础url
IMAGE_BASE_URL=http://192.168.43.163/images
  然后接下来考虑怎么读取这个配置文件,到这步我们可以回忆一下之前我们是怎么读取数据库的配置文件db.properties的。spring给我们提供了完整的解决方案,不会的伙伴spring可得好好学了哟。考虑细节,这里我就多说一点吧。

spring读取信息这部分是在之前写的xml文件里,这里我截个图给大家看下大家就明白了。

然后就是spring读取文件信息了,这里就要用到@Value("$文件的key字段")这个知识点了,当你在写java代码声明这个字段的时候spring会给你自动注入进去的。好,直接来给大家写好代码,毕竟得要干货嘛。

代码六

package com.taotao.service.impl;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import com.taotao.common.utils.FtpUtil;
import com.taotao.common.utils.IDUtils;
import com.taotao.service.PictureService;

/**
 * 
  * @ClassName: PictureServiceImpl  
  * @Description: TODO(图片上传服务)  
  * @author 汪本成  
  * @date 2016年8月9日 下午12:02:33  
  *
 */
@Service
public class PictureServiceImpl implements PictureService 
	
	//注入resource.properties的Key字段
	@Value("$FTP_IP")
	private String FTP_IP;
	@Value("$FTP_PORT")
	private Integer FTP_PORT;
	@Value("$FTP_USERNAME")
	private String FTP_USERNAME;
	@Value("$FTP_PASSWORD")
	private String FTP_PASSWORD;
	@Value("$FTP_BASE_PATH")
	private String FTP_BASE_PATH;
	@Value("$IMAGE_BASE_URL")
	private String IMAGE_BASE_URL;
	
	@Override
	public Map<?, ?> uploadFile(MultipartFile uploadFile) 
		Map resultMap = new HashMap<>();
		try 
		//生成一个新的文件名
		//取原始文件名
		String oldName = uploadFile.getOriginalFilename();
		
		//生成新文件名
		//UUID.randomUUID();
		String newName = IDUtils.genImageName();
		newName = newName + oldName.substring(oldName.lastIndexOf("."));
		
		//图片上传
		String imagePath = new DateTime().toString("/yyyy/MM/dd");
		boolean result = FtpUtil.uploadFile(FTP_IP, FTP_PORT, FTP_USERNAME, FTP_PASSWORD, FTP_BASE_PATH,
				imagePath, newName, uploadFile.getInputStream());
		
		//返回结果
		if(!result) 
			resultMap.put("error", 1);
			resultMap.put("message", "文件上传失败");
			return resultMap;
			
		
		resultMap.put("error", 0);
		resultMap.put("url", IMAGE_BASE_URL + imagePath + "/" + newName);
		return resultMap;
		
		 catch (IOException e) 
			resultMap.put("error", 1);
			resultMap.put("message", "文件上传异常");
			return resultMap;
		
		
	



4.3、Controller

功能:接收页面传递过来的图片。调用service上传到图片服务器。返回结果。

参数:MultiPartFileuploadFile

返回值:返回json数据,应该返回一个pojo,PictureResult对象。

代码七

package com.taotao.controller;

import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import com.taotao.service.PictureService;

@Controller
public class PictureController 

	@Autowired
	private PictureService pictureService;
	
	@RequestMapping("/pic/upload")
	@ResponseBody
	public Map<?, ?> pictureUpload(MultipartFile uploadFile) 
		Map<?, ?> result = pictureService.uploadFile(uploadFile);
		return result;
	


然后更新一下taotao-common这个工程。启动taotao-manager,点击上传图片。

4.4、图片上传异常


控制台输出一下异常信息:

错误分析:缺少配置文件

错误解决:1、需要引入file-up;oad和common-io包;

                 2、在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>


之后重启taotao-manager,打开qq浏览器,测试下,发现好使,如图:

但是很遗憾,在火狐浏览器上却失败了

这是为什么呢,我觉得这就是插件本身的兼容性问题。但是问题出来了我们必须得解决呀,怎么解决呢?

这里就要换个思路,统一换成利用json数据来返回就ok。这里我就写个json的工具类放到taotao-common下。

代码九

package com.taotao.common.utils;

import java.util.List;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * 
  * @ClassName: JsonUtils  
  * @Description: TODO(淘淘商城自定义响应结构)  
  * @author 汪本成  
  * @date 2016年8月10日 上午1:32:37  
  *
 */
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;
    
    


然后再修改一下我们写的controller就行了

代码十

package com.taotao.controller;

import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import com.taotao.common.utils.JsonUtils;
import com.taotao.service.PictureService;

/**
 * 
  * @ClassName: PictureController  
  * @Description: TODO(图片上传的controller)  
  * @author 汪本成  
  * @date 2016年8月10日 上午1:33:32  
  *
 */
@Controller
public class PictureController 

	@Autowired
	private PictureService pictureService;
	
	@RequestMapping("/pic/upload")
	@ResponseBody
	public String pictureUpload(MultipartFile uploadFile) 
		Map<?, ?> result = pictureService.uploadFile(uploadFile);
		
		//为了保证兼容性,需要把Result转换成json格式的字符串
		String json = JsonUtils.objectToJson(result);
		
		return json;
	


然后启动进行测试:


ok,完美解决图片上传问题,明天继续开发








以上是关于淘淘商城---8.9的主要内容,如果未能解决你的问题,请参考以下文章

淘淘商城_0100_前言

CK003-淘淘商城实战高并发分布式项目(新版)

新版淘淘商城_01_简介

淘淘商城01——工程介绍及搭建

淘淘商城2

淘淘商城第二天