#上传图片到七牛云,做云存储
com.nowcoder.controller.NewsController:
/**
*
* @param file 向服务器上传的图片的二进制流
* @return
*/
//图片传输的时候就是二进制流,不需要模板渲染,所以用@ResponseBody
@RequestMapping(value = "/uploadImage/",method = RequestMethod.POST)
@ResponseBody
public String uploadImage(@RequestParam("file") MultipartFile file){
try{
//上传图片到本地
//String fileUrl=newsService.saveImage(file);
//上传图片到七牛云
String fileUrl = qiniuService.saveImage(file);
if (fileUrl==null){
return ToutiaoUtil.getJSONString(1, "上传图片失败");
}
return ToutiaoUtil.getJSONString(0, fileUrl);
}catch (Exception e){
logger.error("上传图片失败"+e.getMessage());
return ToutiaoUtil.getJSONString(1,"上传失败");
}
}
com.nowcoder.service.QiniuService:
大致思路:通过上传文件的文件名,检查其后缀(fileExt)是否为服务支持的后缀,然后通过UUID随机产生(String)fileName+fileExt,最后上传的文件在云中的名字:QINIU_IMAGE_DOMAIN+fileName+fileExt。
@Service
public class QiniuService {
private static final Logger logger = LoggerFactory.getLogger(QiniuService.class);
//...生成上传凭证,然后准备上传
//设置好账号的ACCESS_KEY和SECRET_KEY
String accessKey = "S_FdXUJAL7pSpDx-NUo8TaYzj2rJbsEPEAiZ8FrL";
String secretKey = "HjtyyBQEcjrvpqM0zqTo05vcWrNaKD1LzhQDURhf";
String bucket = "toutiao";
private static String QINIU_IMAGE_DOMAIN = "http://pnmtwczgg.bkt.clouddn.com/";
UploadManager uploadManager = new UploadManager();
//密钥配置
Auth auth = Auth.create(accessKey, secretKey);
//核心方法
public String saveImage(MultipartFile file) throws IOException {
//简单上传,使用默认策略,只需要设置上传的空间名就可以了
String upToken = auth.uploadToken(bucket);
try {
int dotPos = file.getOriginalFilename().lastIndexOf(".");
if (dotPos < 0) {
return null;
}
String fileExt = file.getOriginalFilename().substring(dotPos + 1).toLowerCase();
if (!ToutiaoUtil.isFileAllowed(fileExt)) {
return null;
}
//文件在云上的存储名
String fileName = UUID.randomUUID().toString().replaceAll("-", "") + "." + fileExt;
//调用put方法上传
/**
* file.getBytes():图片的字节流
* fileName:随机生成的图片名
* upToken:你指定存储的云空间
*/
Response response = uploadManager.put(file.getBytes(), fileName, upToken);
//打印返回的信息
if (response.isOK() && response.isJson()) {
return QINIU_IMAGE_DOMAIN + JSONObject.parseObject(response.bodyString()).get("key");
} else {
logger.error("七牛异常:" + response.bodyString());
return null;
}
} catch (QiniuException e) {
// 请求失败时打印的异常的信息
logger.error("七牛异常:" + e.getMessage());
return null;
}
}
}
#优势:
1.图片做单独的服务器,cdn内容分发网络,内容分发到各个节点,能够更快的访问静态文件。
2.云上有封装好的附加功能,比如:可以做实时缩图和实时切图,或者鉴黄。
CDN的全称是Content Delivery Network,即内容分发网络。CDN是构建在网络之上的内容分发网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。
#知识点补充:
##什么是MultipartFile?
MultipartFile是spring类型,代表HTML中form data方式上传的文件,包含二进制数据+文件名称。
什么是CDN?
CDN的全称是==Content Delivery Network==,即内容分发网络。CDN是构建在网络之上的内容分发网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。
#疑问:
##图片上传到云上,其中的过程?
先来看
/**
var oPopupUpload = new PopupUpload({
});
*/
(function (window) {
var PopupUpload = Base.createClass('main.component.PopupUpload');
var Popup = Base.getClass('main.component.Popup');
var Upload = Base.getClass('main.component.Upload');
var Component = Base.getClass('main.component.Component');
var Util = Base.getClass('main.base.Util');
Base.mix(PopupUpload, Component, {
_tpl: [
'<div>',
'<div class="form-group">',
'<div class="form-group">',
'<label class="col-sm-2 control-label">上传图片</label>',
'<div class="js-image-container col-sm-10">',
'<a href="javascript:void(0);" class="btn btn-info btn-upload js-upload-btn" style="diplay:inline-block;position:relative;">上传图片</a>',
'</div>',
'</div>',
'<div class="form-group"><label class="col-sm-2 control-label">标题</label><div class="col-sm-10"><input class="js-title form-control" type="text"></div></div>',
'<div class="form-group"><label class="col-sm-2 control-label">链接</label><div class="col-sm-10"><input class="js-link form-control" type="text"></div></div>',
'<div class="form-group">',
'<div class="col-lg-10 col-lg-offset-2">',
'<input type="submit" value="提交" class="js-submit btn btn-default btn-info">',
'</div>',
'</div>',
'</div>'].join(''),
listeners: [{
name: 'render',
type: 'custom',
handler: function () {
var that = this;
var oEl = that.getEl();
var oUploadBtn = oEl.find('a.js-upload-btn');
new Upload({
targetEl: oUploadBtn,
url: '/uploadImage/',
check: function (oFile, sType, nFileSize) {
var sMsg = nFileSize === 0 ? '文件大小不能为0' : /image/gi.test(sType || '') ? '' : '文件格式不正确';
sMsg && alert(sMsg);
return !sMsg;
},
call: function (oResult) {
var sUrl = $.trim(oResult.msg);
if (oResult.code !== 0) {
return alert('出现错误,请重试');
}
that.image = sUrl;
that.showImage(sUrl);
}
});
}
}, {
name: 'click input.js-submit',
handler: function () {
var that = this;
var oEl = that.getEl();
var sTitle = $.trim(oEl.find('input.js-title').val());
var sLink = $.trim(oEl.find('input.js-link').val());
if (!sTitle) {
return alert('标题不能为空');
}
if (!sLink) {
return alert('链接不能为空');
}
if (!that.image) {
return alert('图片不能为空');
}
if (that.requesting) {
return;
}
that.requesting = true;
$.ajax({
url: '/user/addNews/',
method: 'post',
data: {image: that.image, title: sTitle, link: sLink},
dataType: 'json'
}).done(function (oResult) {
that.emit('done');
}).fail(function (oResult) {
alert('出现错误,请重试');
}).always(function () {
that.requesting = false;
});
}
}]
})(window);
我可以看到:有2个function,一个是触发URL:/uploadImage/;一个触发:/user/addNews/;
触发的url与NewsController中方法的url对应,如/user/addNews/与addNews方法上的@RequestMapping(path={“/user/addNews/“}对应。
通俗点讲,
- 当点击上传图片,并选择图片确定,这时会执行/uploadImage/方法,并返回经过处理的JSON串,JSON串=带有七牛外连接+图片名字(UUID生成)+后缀名。前端收到后端返回的JSON串,根据code值来判断是否上传成功。这是一个典型的前端和后端的交互根据json串。
- 当我们点击提交时,前端会将访问URL锁定在/user/addNews/下,并使用POST方法,将data域中的image(就是步骤1中图片的路径名),title和link与addNews方法中的参数绑定,进而将这条咨询添加到数据库中。
com.nowcoder.controller.NewsController:
/**
*
* @param file 向服务器上传的图片的二进制流
* @return
*/
//图片传输的时候就是二进制流,不需要模板渲染,所以用@ResponseBody
@RequestMapping(value = "/uploadImage/",method = RequestMethod.POST)
@ResponseBody
public String uploadImage(@RequestParam("file") MultipartFile file){
try{
//上传图片到本地
//String fileUrl=newsService.saveImage(file);
//上传图片到七牛云
String fileUrl = qiniuService.saveImage(file);
if (fileUrl==null){
return ToutiaoUtil.getJSONString(1, "上传图片失败");
}
return ToutiaoUtil.getJSONString(0, fileUrl);
}catch (Exception e){
logger.error("上传图片失败"+e.getMessage());
return ToutiaoUtil.getJSONString(1,"上传失败");
}
}
//增加咨询
@RequestMapping(value = "/user/addNews/",method = RequestMethod.POST)
@ResponseBody
public String addNews(@RequestParam("image") String image,
@RequestParam("title") String title,
@RequestParam("link") String link){
try {
News news = new News();
news.setCreatedDate(new Date());
news.setTitle(title);
news.setImage(image);
news.setLink(link);
if (hostHolder.getUser() != null) {
news.setUserId(hostHolder.getUser().getId());
}
//设置了一个匿名用户
else {
news.setUserId(3);
}
newsService.addNews(news);
return ToutiaoUtil.getJSONString(0);
}catch (Exception e){
logger.error("添加失败"+e.getMessage());
return ToutiaoUtil.getJSONString(1,"添加失败!");
}
}
com.nowcoder.util.ToutiaoUtil:
返回JSON串的处理函数:
public static String getJSONString(int code, String msg) {
JSONObject json = new JSONObject();
json.put("code", code);
json.put("msg", msg);
return json.toJSONString();
}
可以看到,json串中有2个key,code和msg。
##操作效果图:
使用postman:
可以看到uploadImage方法返回的json串:
{“msg”:”http://pp5tb1vb6.bkt.clouddn.com/eb9b3019211242e6928fdc94a6c8fda3.jpg","code":0}
前端根据”code”键值对判断是否上传成功;上传成功(就是”code”:0)后,通过”msg”取得图片在云上存储的位置,将其作为url:/user/addNews/对应方法addNews(String image,String title,String link)中参数image,传入方法中。
##在云上的存储情况:
在数据库的存储情况: