前言
这个功能吧,我想了很久。以前用一个很蠢的方案实现过,但是不够优雅漂亮。哎,你猜怎么着,我最近琢磨出了一个比较靠谱的套路。走着
前端
上传组件
<Upload
ref="uploadImage"
type="drag"
:action="`${saveAction}`"
:data="{filename: currentFileName}"
accept="image/png, image/jpeg, image/jpg"
:max-size="2048"
:format="['jpg','jpeg','png']"
:on-success="handleSuccess"
:before-upload="beforeUpload"
:on-error="handleError"
:with-credentials="true"
>
<div style="padding: 20px 0">
<Icon
type="ios-cloud-upload"
size="52"
style="color: #3399ff"
></Icon>
<p style="text-align: center">1.点击或将文件拖拽到这里</p>
</div>
</Upload>
其中saveAction是后端对应的接口地址,比如/img/upload。:data则是访问时带的参数,accept规定可用文件类型。
比较关键的是before-upload这个方法,它会阻断上传的过程,转而成为手动,这样的好处是如果上传的这个文件你想换,就可以手动删掉重新选
// 获取真实文件,.name是文件原始名称
beforeUpload(file) {
this.files = file
this.currentFileName = file.name
return false
},
删除部分
<p
v-if="files !== null"
style="margin: 10px"
>
{{ files.name }}
<span style="margin-left: 20px">
<Button
type="error"
size="small"
@click="removeItem()"
>2.是否删除</Button>
</span>
</p>
对应的js
removeItem() {
this.files = null
},
最终上传:
<Button
type="success"
size="small"
:disabled="files === null"
@click="confirmUpload"
>3.确认提交</Button>
对应的js
confirmUpload() {
this.$Modal.confirm({
title: '确定提交吗?',
onOk: () => {
this.$refs.uploadImage.post(this.files)
},
onCancel: () => {
this.$Message.warning('已取消')
},
})
},
这里我就要吹一下自己了,翻阅源码时发现iview原本的上传就是用内置的ajax的post方法实现的,这里我直接跳过了源码的封装,把post拿出来用,效果还不错~
后端
后端我琢磨了挺久,一直没找到特别好的办法。主要是我原本的思路是把file作为一个完整的文件传上去,但是后来我发现COS的源码里可以接受输入流,一下子反应过来可以把文件转成InputStream再上传,一下子优雅了很多:
/**
* 上传图片
*
* @param file 文件原件
* @param creator 创建人
* @param filename 文件名
* @return
*/
@ApiOperation(value = "上传图片")
@PostMapping("/uploadImage/{creator}")
public ApiReturn<String> uploadHeadImage(
@RequestParam(value = "file") MultipartFile file,
@PathVariable String creator,
@RequestParam String filename
) {
byte[] bytes;
try {
// 把文件转成byte数组
bytes = file.getBytes();
} catch (Exception e) {
log.error("Upload file error", e);
return new ApiReturn<>(e.getMessage());
}
// 再把byte数组转成输入流
InputStream uploadStream = new ByteArrayInputStream(bytes);
URL url = null;
try {
// 这里调用了我自己编写的COS工具,下面会写
url = cosUtil.saveFile(filename, uploadStream);
} catch (Exception e) {
log.error(e.getMessage());
return new ApiReturn<>(e.getMessage());
}
// 如果上传成功,把url落表,如果落表失败,把云上的图也删掉
if (url != null) {
int count = blackService.saveImage(ImageRecord.builder()
.url(url.toString())
.name(filename)
.creator(StringUtils.hasText(creator) ? creator : "")
.createTime(new Date())
.build());
if (count == 0) {
// 存储失败,把云服务中的该图片删除
cosUtil.delFile(filename);
return new ApiReturn<>("未知原因存储失败");
}
}
return new ApiReturn<>(url.toString());
}
附:腾讯COS专用工具类
@Component
@Slf4j
public class CosUtil {
/**
* Cos桶位名称
*/
@Value("${cos.bucket-name}")
private String bucketName;
/**
* 存取桶
*/
@Value("${cos.bucket}")
private String bucket;
/**
* COS物理位置
*/
@Value("${cos.bucket-position}")
private String bucketPosition;
/**
* cos调用人唯一标识
*/
@Value("${cos.secret-id}")
private String secretId;
/**
* cos密钥
*/
@Value("${cos.secret-key}")
private String secretKey;
/**
* App唯一标识
*/
@Value("${cos.app-id}")
private String appId;
/**
* 转换管理器
*/
private TransferManager transferManager;
/**
* Cos客户端
*/
private COSClient cosClient;
/**
* 初始化client
*/
@PostConstruct
private void init() {
log.info("初始化COS Client中,参数为: {}",
String.format("bucketName: %s,appid: %s, bucket: %s", bucketName, appId, bucket));
Region region = new Region(bucketPosition);
cosClient = new COSClient(new BasicCOSCredentials(secretId, secretKey), new ClientConfig(region));
createBucket();
ExecutorService threadPool = getThreadPool();
// 传入一个 threadpool, 若不传入线程池, 默认 TransferManager 中会生成一个单线程的线程池。
transferManager = new TransferManager(cosClient, threadPool);
}
private void createBucket() {
if (cosClient.listBuckets().size() == 0) {
CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);
// 设置 bucket 的权限为PublicRead(公有读私有写)
createBucketRequest.setCannedAcl(CannedAccessControlList.PublicRead);
try {
cosClient.createBucket(createBucketRequest);
} catch (CosClientException serverException) {
serverException.printStackTrace();
}
}
}
private ExecutorService getThreadPool() {
return new ThreadPoolExecutor(
Runtime.getRuntime().availableProcessors(),
200,
0L,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(1024),
new ThreadFactoryBuilder()
.setNameFormat("demo-pool-%d").build(),
new ThreadPoolExecutor.AbortPolicy()
);
}
/**
* 使用文件上传
*
* @param key 文件主键
* @param file 文件主体
* @return 可用链接
* @throws InterruptedException 抛出异常
*/
public URL saveFile(String key, File file) throws InterruptedException {
transferManager.upload(
new PutObjectRequest(bucketName, String.format("%s/%s", bucket, key), file)
).waitForUploadResult();
// 关闭 TransferManger
transferManager.shutdownNow();
return cosClient.generatePresignedUrl(bucketName, String.format("%s/%s", bucket, key),
new Date(System.currentTimeMillis() + 5 * 60 * 10000));
}
/**
* 使用输入流上传
*
* @param key 文件主键
* @param inputStream 输入流
* @return 可用链接
* @throws InterruptedException 抛出异常
*/
public URL saveFile(String key, InputStream inputStream) throws InterruptedException, ParseException {
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setExpirationTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2030-01-01 23:59:59"));
transferManager.upload(bucketName,
String.format("%s/%s", bucket, key),
inputStream, objectMetadata).waitForUploadResult();
return cosClient.generatePresignedUrl(bucketName, String.format("%s/%s", bucket, key),
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2030-01-01 23:59:59"));
}
/**
* 获取文件
*
* @param key
* @return
*/
public COSObject getCosObject(String key) {
return cosClient.getObject(bucketName, key);
}
/**
* 文件是否存在
*
* @param key
* @return
*/
public boolean ifImageExist(String key) {
return cosClient.doesObjectExist(bucketName, String.format("%s/%s", bucket, key));
}
/**
* 刷新图片URL
*
* @param key 文件名
* @return 刷新后的URL
*/
public URL getUpdateFileUrl(String key) {
return cosClient.generatePresignedUrl(bucketName, String.format("%s/%s", bucket, key),
new Date(System.currentTimeMillis() + 31104 * 1000000L));
}
/**
* 删除文件
*
* @param key 文件主键
*/
public void delFile(String key) {
cosClient.deleteObject(bucketName, String.format("%s/%s", bucket, key));
}
}
吐槽
腾讯COS的文档真的不大靠谱,如果你想自己写工具类,强烈建议对照源码找API,不要用文档的API。
本文首发于CSDN,作者:亭台烟雨中
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/153514.html