基于MAVEN的SSM+bootstrap的电商测试项目搭建日志(二)

最近忙着毕业搬东西,还有搭建nginx环境什么的,巨忙也巨烦,每次再开展新的一个模块的时候总会出现一些神奇的错误,不过经过最近的努力,还是比较出色的完成了商品后台的商品上传(包括图片传)的功能,以下就是对之前的知识点总结:

Linux centOS7的安装部署:

为什么要用到linux呢?因为一般做项目的应该都知道,当一个网站需要反复的读取图片的时候,就不能把图片放在数据库中,比较好的选择就是先存入tomcat服务器,然后再上传到ftp上,通过nginx建立一个http,再通过http读取相应的图片文件,至于再深的我也没有再做进一步的了解,还处于会用的阶段。

需要搭建centos的话,我选择使用vm虚拟机来搭建,使用了centos7 32位系统,在安装过程中,要注意启动ens33的连接服务,否则可能会导致linux无法连接网络。在磁盘分配方面,/boot的内存大小不要小于200mb,默认的软件可以按照需求选择安装(我是使用了基础服务器的配置)。

secureCRT的连接:

由于linux的一些特性,导致在windows上复制的指令没办法粘贴到虚拟机中,有一个办法是使用vm自带的tools软件,而如果linux没有安装图形化界面的话,安装实在是显得过于麻烦,但linux如果安装了图形化界面总觉得有些不妥(不够逼格),所以就是使用远程连接软件进行操作。在安装完软件准备连接时,要先查看linux的ip,使用ifconfig查看ip,使用这个ip地址进行连接,成功连接后,就能开心的复制粘贴了。

nginx的安装:

nginx是一个高性能的反向代理服务器,如果单纯的使用ftp上传图片时,很有可能会暴露账号和密码信息,这是不安全的,所以通过nginx的反向代理来实现http的搭建。在安装前要注意需要yum gcc和c++的前置软件,还有另外四个我不记得了,可以查一下,如何具体安装就不在这里阐述了。安装完成后,需要配置conf配置文件,使用whereis nginx寻找根目录,使用vi进入nginx/conf/nginx.conf ,修改文件,首先更改权限用户,一般在第一行

user root;
然后在下面寻找到server,如果没有可以手动创建,增加location命令在里面写入
root /var/ftp/

这是为之后安装ftp的映射做准备。

之后保存退出编辑,在nginx的sbin目录中使用./nginx -t查看配置是否正确,如果正确,那么./nginx -s reload重新载入 就好。

vsftpd的安装和配置:

vsftpd是一个可以运行在linux上的一个服务器,支持文件的上传和下载,具体安装不做具体阐述,需要说明的是需要在vsftpd的配置文件最后,注明需要修改的默认地址,例如之前在nginx配置的/var/ftp,并且使用chmod修改Var和ftp以及子目录的权限,这样有利于filezilla进行读写。

项目中的配置:

图片上传的环境搭好后,可以来完成后台的功能代码实现,首先我使用了bootstrap简单了实现了模态框的功能,为了实现添加商品的功能,在商品类目的选择上,我是用了ajax遍历类目数据库的形式,再基本的输入框都完成后,首先要做的就是实现图片的上传功能,这里可以使用现成的ftputil通用工具(是从别人那里拷的),代码如下:

package com.amake.ftphelper;

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;

/**
 * -------------------------------------------
 * Title : FtpUtil 
 * Description : ftp上传下载工具类
 * Create on : 2017年4月4日 上午11:14:09
 * Copyright (C) strongunion
 * @author RICK
 * 修改历史: 
 * 修改人 修改日期 修改描述
 * -------------------------------------------
 */
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("C:\\Users\\RICK\\Pictures\\Saved Pictures\\fendou.jpg"));  
//            boolean flag = FtpUtil.uploadFile("192.168.2.37", 21, "ftpuser", "ftpuser", "/home/ftpuser/images", "/2017/01/16", "hello.jpg", in);
//            System.out.println(flag);  
//        } catch (FileNotFoundException e) {  
//            e.printStackTrace();  
//        }  
//    }
}
然后开始分析图片的上传,在dao层中不涉及,所以可以不用对pojo和mapper类进行添加类文件,要考虑的是在service层中添加图片上传的事务管理,主要的逻辑为:连接ftp服务器,添加图片上传路径,修改图片的名字,合成http路径,将得到的值放入map中,代码如下:
package com.amake.simpleShop.service.Impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;

import com.amake.ftphelper.FtpUtil;
import com.amake.idhelper.IDUtil;
import com.amake.simpleShop.service.PictureService;
import com.amake.simpleshop.pojo.itemInfo;

@Service
public class PictureServiceImpl implements PictureService {
	@Value("${FTP.ADDRESS}")
	private String ftpAddress;
	@Value("${FTP.USERNAME}")
	private String ftpUsername;
	@Value("${FTP.PASSWORD}")
	private String ftpPassword;
	@Value("${FTP.BASEPATH}")
	private String ftpBasepath;
	@Value("${FTP.PORT}")
	private String ftpPort;
	@Value("${IMG.URL}")
	private String imgUrl;

	@Override
	public Map uploadPicture(MultipartFile uploadFile) {
		// Map resultMap = new HashMap<>();
		// FtpUtil ftpUtil = new FtpUtil();
		// try {
		// String oldName = uploadFile.getOriginalFilename();
		// String newName = IDUtil.genImageName() +
		// oldName.substring(oldName.lastIndexOf("."));
		// String imgpath = new DateTime().toString("/yyyy/MM/dd");
		// int port = 0 ;
		// port = Integer.valueOf(ftpPort);
		// boolean result = ftpUtil.uploadFile("192.xxx.xxx.xxx", 21, "ftpuser",
		// "123456", "/var/ftp/image/", "/2018/6/24", "test.jpg",new FileInputStream(new
		// File("G:\\壁纸\\test.jpg")));
		// if(!result) {
		// resultMap.put("error", 1);
		// resultMap.put("message", "文件上传失败");
		// return resultMap;
		// }
		// resultMap.put("error", 0);
		// resultMap.put("url", imgUrl + imgpath+"/" + newName);
		// } catch (Exception e) {
		// resultMap.put("error", 1);
		// resultMap.put("message", "文件上传发生异常");
		// return resultMap;
		// }
		// return resultMap;
		Map resultMap = new HashMap<>();
		try {

			FtpUtil ftpUtil = new FtpUtil();
			String name = uploadFile.getOriginalFilename();
			String newname = IDUtil.genImageName();
			newname = newname + name.substring(name.lastIndexOf("."));
			int port = Integer.valueOf(ftpPort);
			String time = new DateTime().toString("/yyyy/MM/dd");
			boolean result = ftpUtil.uploadFile(ftpAddress, port, ftpUsername, ftpPassword, ftpBasepath, time, newname,
					uploadFile.getInputStream());
			if (!result) {
				resultMap.put("error", 1);
				resultMap.put("message", "上传失败");
			}
			resultMap.put("error", "0");
			resultMap.put("itmImg", imgUrl + time + newname);
			return resultMap;
		} catch (Exception e) {
			resultMap.put("error", 1);
			resultMap.put("message", "上传异常");
			return resultMap;
		}

	}


}

其中没有直接把ftp的连接信息直接写入代码,因为文件上传属于通用类,而既然是通用类其配置也可能不一样,所以要把连接信息写入一个properties文件中,把springmvc中的配置文件加载扫描的文件名改为*.properties,这样就可以把所有配置文件全部读取到,使用@Value注解即可将配置信息注入到下面的字符串中。

之后是写controller层,要实现的逻辑就是接收文件调用service方法上传图片,并且返回一个json对象。

package com.amake.simpleshop.controller;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;

import com.amake.common.JsonUtils;
import com.amake.simpleShop.service.PictureService;
import com.amake.simpleshop.pojo.itemInfo;

@Controller
public class pictureController {
	@Autowired
	PictureService pictureService;
	@RequestMapping("/picUpload")
	@ResponseBody
	public String pictureUpload(HttpServletRequest request) {
			MultipartHttpServletRequest servletRequest = (MultipartHttpServletRequest) request;
				MultipartFile multipartFile = servletRequest.getFile("img_group");
				Map result = pictureService .uploadPicture(multipartFile);
				String json = JsonUtils.objectToJson(result);
				return json;
	}
	
//	@RequestMapping("/upload")
//	@ResponseBody
//    public String getParam2(@RequestParam(value = "name_group") String name_group) {
//        return name_group;
//    }
}

然后就是要在jsp页面写逻辑,使用ajax把数据发送到后端,使用post方法传一个json数据,这个时候需要注意的是,input中的name值要与pojo中的值相同,这样框架就可以把从表单中的值直接封装传给后台并保存到数据库。还有图片的路径获取有些麻烦的就是,需要把从前端获取到的json对象转换为json字符串,再传给后台,而后台这个时候接受到的url应该是一个%加两个16进制的数字,这个时候还需要使用Urlencode进行解码,但使用了这个解码之后还会报错,缺不影响正常使用,至于为什么报错还需要之后去解决一下,ajax的源码

$("#add_btn").click(function() {
			$.post("/item/save",$("#type_form").serialize());

			$.ajax({
			url : "/picUpload",
			type : 'POST',
			data : new FormData($("#type_form")[0]),
			dataType : 'JSON',
			cache : false,
			processData : false,
			contentType : false,
			success : function(result) {
				var data = JSON.stringify(result.itmImg);
				$.post("/item/pic",data);
				$('#addItemModal').modal('hide');
				to_page(maxPage);
			console.log(JSON.stringify(result));
			}
		}); 
	});

具体的实现可能还有些细节,等想到了再补充,这样就完成了添加+图片上传的功能模块实现。

你可能感兴趣的:(练习项目:电商)