项目7_ssm_宜立方商城_day4

本文详细介绍了FastDFS的架构组成,包括Trackerserver和Storageserver的角色与功能,以及FastDFS客户端的安装与配置流程。通过具体示例展示了如何使用FastDFS上传图片,并解决了浏览器兼容性问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

FastDFS架构

FastDFS架构包括 Tracker server和Storage server。客户端请求Tracker server进行文件上传、下载,通过Tracker server调度最终由Storage server完成文件上传和下载。
    Tracker server作用是负载均衡和调度
    Storage server作用是文件存储,客户端上传的文件最终存储在Storage服务器上,可以将storage称为存储服务器

服务端两个角色:
Tracker:管理集群,tracker也可以实现集群。每个tracker节点地位平等。
收集Storage集群的状态。
Storage:实际保存文件
Storage分为多个组,每个组之间保存的文件是不同的。每个组内部可以有多个成员,组成员内部保存的内容是一样的,组成员的地位是一致的,没有主从的概念。

文件上传

客户端上传文件后存储服务器将文件ID返回给客户端,此文件ID用于以后访问该文件的索引信息。文件索引信息包括:组名,虚拟磁盘路径,数据两级目录,文件名。

安装好的fastdfs服务器 安装步骤:https://blog.youkuaiyun.com/lushizhuo9655/article/details/98868186

将fastdfs客户端安装到本地仓库 将此项目导入到eclipse中,然后install即可

然后将依赖添加进e3-manager-web的pom文件即可

<dependency>
	 <groupId>fastdfs_client</groupId>
         <artifactId>fastdfs_client</artifactId>
         <version>1.25</version>
</dependency>

通过fastdfs客户端测试上传图片

配置文件client.conf(ip是变化的)

tracker_server=192.168.0.242:22122
public class FastdfsTest {
@Test
public void showUpload() throws Exception{
		// 1、加载配置文件,配置文件中的内容就是tracker服务的地址。这是配置文件的绝对路径
		ClientGlobal.init("E:/20200520/e3-manager-web/conf/client.conf");
		// 2、创建一个TrackerClient对象。直接new一个。
		TrackerClient trackerClient = new TrackerClient();
		// 3、使用TrackerClient对象创建连接,获得一个TrackerServer对象。
		TrackerServer trackerServer = trackerClient.getConnection();
		// 4、创建一个StorageServer的引用,值为null
		StorageServer storageServer = null;
		// 5、创建一个StorageClient对象,需要两个参数TrackerServer对象、StorageServer的引用
		StorageClient storageClient = new StorageClient(trackerServer, storageServer);
		// 6、使用StorageClient对象上传图片。
		//扩展名不带“.”  上传文件的全路径        扩展名            元数据
		String[] strings = storageClient.upload_file("D:/practice/1.jpg", "jpg", null);
		// 7、返回数组。包含组名和图片的路径。
		for (String string : strings) {
			System.out.println(string);
		}
}
}

控制台打印结果

图片上传功能

使用的是KindEditor的多图片上传插件。

KindEditor 4.x 文档

http://kindeditor.net/doc.php

将pom依赖从e3-manager-web的pom中移到common中,然后提供一个FastDFSClient的工具类调用

package cn.e3mall.common.utils;

import org.csource.common.NameValuePair;
import org.csource.fastdfs.ClientGlobal;
import org.csource.fastdfs.StorageClient1;
import org.csource.fastdfs.StorageServer;
import org.csource.fastdfs.TrackerClient;
import org.csource.fastdfs.TrackerServer;

public class FastDFSClient {

	private TrackerClient trackerClient = null;
	private TrackerServer trackerServer = null;
	private StorageServer storageServer = null;
	private StorageClient1 storageClient = null;
	
	public FastDFSClient(String conf) throws Exception {
		if (conf.contains("classpath:")) {
			conf = conf.replace("classpath:", this.getClass().getResource("/").getPath());
		}
		ClientGlobal.init(conf);
		trackerClient = new TrackerClient();
		trackerServer = trackerClient.getConnection();
		storageServer = null;
		storageClient = new StorageClient1(trackerServer, storageServer);
	}
	
	/**
	 * 上传文件方法
	 * <p>Title: uploadFile</p>
	 * <p>Description: </p>
	 * @param fileName 文件全路径
	 * @param extName 文件扩展名,不包含(.)
	 * @param metas 文件扩展信息
	 * @return
	 * @throws Exception
	 */
	public String uploadFile(String fileName, String extName, NameValuePair[] metas) throws Exception {
		String result = storageClient.upload_file1(fileName, extName, metas);
		return result;
	}
	
	public String uploadFile(String fileName) throws Exception {
		return uploadFile(fileName, null, null);
	}
	
	public String uploadFile(String fileName, String extName) throws Exception {
		return uploadFile(fileName, extName, null);
	}
	
	/**
	 * 上传文件方法
	 * <p>Title: uploadFile</p>
	 * <p>Description: </p>
	 * @param fileContent 文件的内容,字节数组
	 * @param extName 文件扩展名
	 * @param metas 文件扩展信息
	 * @return
	 * @throws Exception
	 */
	public String uploadFile(byte[] fileContent, String extName, NameValuePair[] metas) throws Exception {
		
		String result = storageClient.upload_file1(fileContent, extName, metas);
		return result;
	}
	
	public String uploadFile(byte[] fileContent) throws Exception {
		return uploadFile(fileContent, null, null);
	}
	
	public String uploadFile(byte[] fileContent, String extName) throws Exception {
		return uploadFile(fileContent, extName, null);
	}
}
  1. 需要把commons-io、fileupload 的jar包添加到工程中。(目前缺fileupload的依赖)
<!-- 文件上传组件 -->
<dependency>
	<groupId>commons-fileupload</groupId>
	<artifactId>commons-fileupload</artifactId>
</dependency>

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

配置一个上传图片的服务器地址,方便修改resource.properties

IMAGE_SERVER_URL=http://192.168.0.242/

在springmvc.xml中加载次配置文件

<context:property-placeholder location="classpath:conf/resource.properties"/>
/**
* 图片上传管理
*/
@Controller
@ResponseBody
public class PictureController {
@Value("${IMAGE_SERVER_URL}")
private String IMAGE_SERVER_URL;
@RequestMapping("/pic/upload")
public Map uploadPic(MultipartFile uploadFile){
	try {
		//把图片上传到图片服务器
		FastDFSClient fastDFSClient=new FastDFSClient("classpath:conf/client.conf");
		//取文件扩展名
		String originalFilename = uploadFile.getOriginalFilename();
		String extName=originalFilename.substring(originalFilename.lastIndexOf(".")+1);
		//得到一个图片的地址和文件名
		String url = fastDFSClient.uploadFile(uploadFile.getBytes(), extName);//上传内容  扩展名
		//补充为完整的url
		url=IMAGE_SERVER_URL+url;
		Map result=new HashMap<>();
		result.put("error", 0);
		result.put("url", url);
		return result;
	} catch (Exception e) {
		e.printStackTrace();
		Map result=new HashMap<>();
		result.put("error", 1);
		result.put("message", "上传图片失败");
		return result;
	}
}
}

但是这样只能在google浏览器上传成功,别的浏览器会失败,原因是浏览器的兼容性对kindeditor不好

响应Map,注解是responsebody  响应的content-Typeapplication/json
兼容性最好的是text/plain 表示普通文本

提供一个对象和json字符串转换的工具类

package cn.e3mall.common.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;

/**
 * 淘淘商城自定义响应结构
 */
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;
    }
    
}

修改代码,解决浏览器的兼容性问题

指定响应结果的content-type和字符集是utf-8

富文本编辑器的使用方法

KindEditor

http://kindeditor.net/

UEditor:百度编辑器

http://ueditor.baidu.com/website/

纯js开发,跟后台语言没有关系。

使用方法
第一步:在jsp中引入KindEditor的css和js代码。
第二步:在表单中添加一个textarea控件。是一个富文本编辑器的载体。类似数据源。
第三步:初始化富文本编辑器。使用官方提供的方法初始化。
第四步:取富文本编辑器的内容。
表单提交之前,把富文本编辑器的内容同步到textarea控件中。

<link href="/js/kindeditor-4.1.10/themes/default/default.css" type="text/css" rel="stylesheet">
<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>

商品添加功能实现

返回值:
Json数据。应该包含一个status的属性。
可以使用E3Result,放到e3-common中。

package cn.e3mall.common.utils;

import java.util.List;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * 淘淘商城自定义响应结构
 */
public class E3Result  implements Serializable{

    // 定义jackson对象
    private static final ObjectMapper MAPPER = new ObjectMapper();

    // 响应业务状态
    private Integer status;

    // 响应消息
    private String msg;

    // 响应中的数据
    private Object data;

    public static E3Result build(Integer status, String msg, Object data) {
        return new E3Result(status, msg, data);
    }

    public static E3Result ok(Object data) {
        return new E3Result(data);
    }

    public static E3Result ok() {
        return new E3Result(null);
    }

    public E3Result() {

    }

    public static E3Result build(Integer status, String msg) {
        return new E3Result(status, msg, null);
    }

    public E3Result(Integer status, String msg, Object data) {
        this.status = status;
        this.msg = msg;
        this.data = data;
    }

    public E3Result(Object data) {
        this.status = 200;
        this.msg = "OK";
        this.data = data;
    }

//    public Boolean isOK() {
//        return this.status == 200;
//    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    /**
     * 将json结果集转化为TaotaoResult对象
     * 
     * @param jsonData json数据
     * @param clazz TaotaoResult中的object类型
     * @return
     */
    public static E3Result formatToPojo(String jsonData, Class<?> clazz) {
        try {
            if (clazz == null) {
                return MAPPER.readValue(jsonData, E3Result.class);
            }
            JsonNode jsonNode = MAPPER.readTree(jsonData);
            JsonNode data = jsonNode.get("data");
            Object obj = null;
            if (clazz != null) {
                if (data.isObject()) {
                    obj = MAPPER.readValue(data.traverse(), clazz);
                } else if (data.isTextual()) {
                    obj = MAPPER.readValue(data.asText(), clazz);
                }
            }
            return build(jsonNode.get("status").intValue(), jsonNode.get("msg").asText(), obj);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 没有object对象的转化
     * 
     * @param json
     * @return
     */
    public static E3Result format(String json) {
        try {
            return MAPPER.readValue(json, E3Result.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Object是集合转化
     * 
     * @param jsonData json数据
     * @param clazz 集合中的类型
     * @return
     */
    public static E3Result formatToList(String jsonData, Class<?> clazz) {
        try {
            JsonNode jsonNode = MAPPER.readTree(jsonData);
            JsonNode data = jsonNode.get("data");
            Object obj = null;
            if (data.isArray() && data.size() > 0) {
                obj = MAPPER.readValue(data.traverse(),
                        MAPPER.getTypeFactory().constructCollectionType(List.class, clazz));
            }
            return build(jsonNode.get("status").intValue(), jsonNode.get("msg").asText(), obj);
        } catch (Exception e) {
            return null;
        }
    }

}

 

1、生成商品id
实现方案:
a)Uuid,字符串,不推荐使用。
b)数值类型,不重复。日期+时间+随机数20160402151333123123
c)毫秒值+随机数。可以使用。
d)使用redis。Incr。推荐使用。

生成商品id的工具类

package cn.e3mall.common.utils;

import java.util.Random;
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());
	}
}

Dao层
向tb_item, tb_item_desc表中插入数据
可以使用逆向工程

Service层
参数:TbItem item,String desc
业务逻辑:略,参加上面
返回值:E3Result

@Service
public class ItemServiceImpl implements ItemService {
@Autowired
private TbItemMapper itemMapper;
@Autowired
private TbItemDescMapper tbItemDescMapper;

public E3Result saveItem(TbItem tbItem, String desc) {
	// 生成商品id
	long itemId = IDUtils.genItemId();
	// 2、补全TbItem对象的属性
	tbItem.setId(itemId);
	//商品状态1-正常,2-下架,3-删除
	tbItem.setStatus((byte)1);
	Date date=new Date();
	tbItem.setCreated(date);
	tbItem.setUpdated(date);
	itemMapper.insert(tbItem);
	// 4、创建一个TbItemDesc对象
	TbItemDesc tbItemDesc=new TbItemDesc();
	// 5、补全TbItemDesc的属性
	tbItemDesc.setItemId(itemId);
        tbItemDesc.setItemDesc(desc);
	tbItemDesc.setCreated(date);
	tbItemDesc.setUpdated(date);
	tbItemDescMapper.insert(tbItemDesc);
	return E3Result.ok();
}
@Controller
public class ItemController {
	@Autowired
	private ItemService itemService;
	@RequestMapping(value="item/save",method=RequestMethod.POST)
	@ResponseBody
	public E3Result saveItem(TbItem tbItem,String desc){
		E3Result result =itemService.saveItem(tbItem, desc);
		return result;
	}	
}

商品修改、商品删除、上架下架。以后有空在补上(比较简单目前就不写了)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

guoyebing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值