俄罗斯b2b网站大全/b站免费版入口
客户端上传例子:用户上传头像;
服务器直传在上节课中也讲到过,服务器将自动生成的图片,直接交给云服务器。
我们选择 qiniu云服务器,因为免费。
官网:
注册会比较麻烦,因此我们直接截图吧
创建云服务器 08:23
新建存储空间,利用域名来访问:
现在什么都没有
又新建一个存储空间,域名为:
在porm中导入qiniu所需的包
接下来在 application Properties中
做一些配置
AK用来标识用户身份,不是谁都可以往空间中传东西;
SK用来上传具体内容时为内容加密
需要将这两个在配置文件中设置。
# qiniu
# 用户身份密钥
qiniu.key.access=6RA-Uus95ZT_1znMrCMD8BpqfjT-K7OKmQTfKB48
# 文件内容加密的密钥
qiniu.key.secret=kPNnLFz2_tzztKUVpSLm0lYngtuHWyIq5LzTmLIL
# 分配的云空间名字以及域名
qiniu.bucket.header.name=community_header
quniu.bucket.header.url=http://pvghrij81.bkt.clouddn.com
# 分配第二个云空间的名字以及域名
qiniu.bucket.share.name=community_share
qiniu.bucket.share.url=http://pvghvvuzm.bkt.clouddn.com
接下来处理上传头像的逻辑:
上传头像的请求在UserController中:
@Value("${qiniu.key.access}")
private String accessKey;@Value("${qiniu.key.secret}")
private String secretKey;@Value("${qiniu.bucket.header.name}")
private String headerBucketName;@Value("${quniu.bucket.header.url}")
private String headerBucketUrl;
@LoginRequired
@RequestMapping(path = "/setting", method = RequestMethod.GET)
public String getSettingPage(Model model) {// 生成 上传文件名称String fileName = CommunityUtil.generateUUID();//文件名称 生成 随机的(同名的话后者会覆盖前者,不能实现存留历史数据的功能)// 设置响应信息StringMap policy = new StringMap();policy.put("returnBody", CommunityUtil.getJSONString(0));//响应体希望响应什么内容,七牛云客户端直传通常采用异步方式来传,因为异步返回JSON字符串,告诉我们有没有成功即可(成功的时候返回0),这样更方便,如果是同步的方式,响应个html,就乱套了// 生成上传凭证(提供上传文件的名称),使得qiniu可以识别身份Auth auth = Auth.create(accessKey, secretKey);//利用 与身份有关的accessKey,与内容有关的secretKey 生成的auth对象,以生成上传凭证uploadTokenString uploadToken = auth.uploadToken(headerBucketName, fileName, 3600, policy);//headerBucketName上传空间名字;fileName文件名;key过期时间1h。policy响应信息model.addAttribute("uploadToken", uploadToken);model.addAttribute("fileName", fileName);return "/site/setting";
}
// 更新头像路径,当我们把数据提交给qiniu云以后,qiniu给我们返回一个消息0,代表成功,成功后要把user表里面的headurl做一个更新,更新为qinniu云的路径,如果不更的话,表里的路径没变,就白传了,网页还是显示的之前的头像,而不是传到qinniu云里面的
@RequestMapping(path = "/header/url", method = RequestMethod.POST)//因为要提交数据,更新数据,所以是POST
@ResponseBody
public String updateHeaderUrl(String fileName) {if (StringUtils.isBlank(fileName)) {return CommunityUtil.getJSONString(1, "文件名不能为空!");//1表示错误}//不为空 则需要拼URLString url = headerBucketUrl + "/" + fileName;//头像有效的访问路径userService.updateHeader(hostHolder.getUser().getId(), url);return CommunityUtil.getJSONString(0);
}
重构好user controller了,因为废弃了原有的几个方法,对现有的方法进行改进
接下来处理对应的表单 setting.html
注释掉:
修改为:
<!--上传到七牛云--><form class="mt-5" id="uploadForm"> <!--异步上传不需要method="post" enctype="multipart/form-data" th:action="@{/user/upload}"--><div class="form-group row mt-4"><label for="head-image" class="col-sm-2 col-form-label text-right">选择头像:</label><div class="col-sm-10"><div class="custom-file"><input type="hidden" name="token" th:value="${uploadToken}"><input type="hidden" name="key" th:value="${fileName}"><input type="file" class="custom-file-input" id="head-image" name="file" lang="es" required=""><label class="custom-file-label" for="head-image" data-browse="文件">选择一张图片</label><div class="invalid-feedback">该账号不存在!</div></div></div></div><div class="form-group row mt-4"><div class="col-sm-2"></div><div class="col-sm-10 text-center"><button type="submit" class="btn btn-info text-white form-control">立即上传</button></div></div></form>
新建setting.js
$(function(){ <!--页面加载完 后调用此function-->$("#uploadForm").submit(upload);<!--当点击提交按钮,触发表单的提交事件时,该事件由upload处理。Form id是uploadForm,当其触发提交事件时-->
});function upload() {$.ajax({url: "http://upload-z1.qiniup.com",<!-- 表示请求要提交给谁-->method: "post",<!-- 请求方式是post-->processData: false,<!-- 不要把表单内容转为字符串,默认情况下,浏览器会把表单内容转为字符串 提交给服务器-->contentType: false,<!-- contentType按理说应该是 html、json。即数据类型,false的意思是不让 jqury设置上传类型,浏览器自动设置。文件和别的数据不一样,是二进制的,当其和别的数据混编在一起时,边界如何确定呢;浏览器会给其加一个随机边界字符串,好去拆分 -->data: new FormData($("#uploadForm")[0]),success: function(data) {<!--成功的时候处理:-->if(data && data.code == 0) {<!--data存在且data.code = 0-->// 更新头像访问路径(异步)$.post(CONTEXT_PATH + "/user/header/url",{"fileName":$("input[name='key']").val()},<!--要传数据的名字-->function(data) {data = $.parseJSON(data);//将data解析为JSONif(data.code == 0) {//表示成功window.location.reload();//刷新页面,看看头像是否改变} else {alert(data.msg);//失败则给出提示}});} else {alert("上传失败!");}}});return false; <!--false表示事件到此为止-->
}
测试:
访问首页、登录:
其头像是:
表里的数据:
现在的路径是本地路径,一回儿要改成 qiniu云的路径
接下来修改头像,上传新的头像。
头像为:
此时 url变为qiniu云的
接下来在 qiniu中查看
可以看到有一条数据
预览没有问题
于是上传头像便解决了。
这只是上传至云服务器的一种方案,通过客户端上传。
接下来演示通过服务器直传。
打开share controller
@Value("${qiniu.bucket.share.url}")//application properties中 qiniu.bucket.share.url
private String shareBucketUrl;
对share函数
文件名(随机,每次都要变,不然很容易重名)
异步生成长图
最终返回浏览器的路径 需要改动
一开始:
改为:
// 生成长图以后,如何访问呢---返回访问路径Map<String, Object> map = new HashMap<>();
// map.put("shareUrl", domain + contextPath + "/share/image/" + fileName);//domain项目域名map.put("shareUrl", shareBucketUrl + "/" + fileName);
打开EventConsumer
@Value("${qiniu.key.access}")
private String accessKey;@Value("${qiniu.key.secret}")
private String secretKey;@Value("${qiniu.bucket.share.name}")
private String shareBucketName;//上传空间名字
@Autowired
private ThreadPoolTaskScheduler taskScheduler;//执行定时任务的线程池,因为要考虑分布式部署的问题
在handleShareMessage中
String htmlUrl = (String) event.getData().get("htmlUrl");//event.getData()是mapString fileName = (String) event.getData().get("fileName");String suffix = (String) event.getData().get("suffix");//后缀//利用上述三个,把命令拼出来String cmd = wkImageCommand + " --quality 75 "+ htmlUrl + " " + wkImageStorage + "/" + fileName + suffix;try {Runtime.getRuntime().exec(cmd);//传入cmd,执行logger.info("生成长图成功: " + cmd);} catch (IOException e) {logger.error("生成长图失败: " + e.getMessage());}//注意,logger.info("生成长图成功: " + cmd); 先执行,因为 Runtime.getRuntime().exec(cmd);//传入cmd,执行 因为图片大,所以慢,所以要等// 启用定时器,监视该图片,每隔一段时间 检测是否生成图片,一旦生成了,则上传至七牛云.UploadTask task = new UploadTask(fileName, suffix);//哪个服务器抢到这个消息,则只有该服务器会开启定时Future future = taskScheduler.scheduleAtFixedRate(task, 500);//taskScheduler.scheduleAtFixedRate(task, 500)触发定时器的执行,定时器启动后有一个返回值 叫future,里面封装了用户的状态task.setFuture(future);
class UploadTask implements Runnable {// 文件名称private String fileName;// 文件后缀private String suffix;// 启动任务的返回值,用于停止定时器private Future future;// 开始时间,方便计算执行时间private long startTime;// 上传次数,保障任务停止private int uploadTimes;public UploadTask(String fileName, String suffix) {this.fileName = fileName;this.suffix = suffix;this.startTime = System.currentTimeMillis();//创建 开始时间}public void setFuture(Future future) {this.future = future;}@Overridepublic void run() {// 生成图片失败if (System.currentTimeMillis() - startTime > 30000) {//时间超过30slogger.error("执行时间过长,终止任务:" + fileName);future.cancel(true);//停止定时器return;}// 上传图片至qiniu云失败if (uploadTimes >= 3) {logger.error("上传次数过多,终止任务:" + fileName);future.cancel(true);//停止定时器return;}String path = wkImageStorage + "/" + fileName + suffix;//本地存放文件完整 路径File file = new File(path);//将path传入以实例化文件fileif (file.exists()) {//如果文件存在logger.info(String.format("开始第%d次上传[%s].", ++uploadTimes, fileName));//%d 位置用++uploadTimes替换;%s用fileName替换// 设置响应信息StringMap policy = new StringMap();policy.put("returnBody", CommunityUtil.getJSONString(0));// 生成上传凭证Auth auth = Auth.create(accessKey, secretKey);String uploadToken = auth.uploadToken(shareBucketName, fileName, 3600, policy);//shareBucketName为空间名;fileName为文件名;3600为过期时间// 指定上传机房UploadManager manager = new UploadManager(new Configuration(Zone.zone1()));//zone1为华北机房try {// 开始上传图片Response response = manager.put(path, fileName, uploadToken, null, "image/" + suffix, false);// 处理响应结果,得到json数据JSONObject json = JSONObject.parseObject(response.bodyString());if (json == null || json.get("code") == null || !json.get("code").toString().equals("0")) {logger.info(String.format("第%d次上传失败[%s].", uploadTimes, fileName));} else {logger.info(String.format("第%d次上传成功[%s].", uploadTimes, fileName));future.cancel(true);//任务结束}} catch (QiniuException e) {logger.info(String.format("第%d次上传失败[%s].", uploadTimes, fileName));}} else {logger.info("等待图片生成[" + fileName + "].");}}
}
测试:
图片越大,越容易失败,百度截图 图片小,第一次就成功了