- 前言
- 前端
- 上传组件
- 后端
- 附:腾讯COS专用工具类
- 吐槽
这个功能吧,我想了很久。以前用一个很蠢的方案实现过,但是不够优雅漂亮。哎,你猜怎么着,我最近琢磨出了一个比较靠谱的套路。走着
前端 上传组件1.点击或将文件拖拽到这里
其中saveAction是后端对应的接口地址,比如/img/upload。:data则是访问时带的参数,accept规定可用文件类型。
比较关键的是before-upload这个方法,它会阻断上传的过程,转而成为手动,这样的好处是如果上传的这个文件你想换,就可以手动删掉重新选
// 获取真实文件,.name是文件原始名称
beforeUpload(file) {
this.files = file
this.currentFileName = file.name
return false
},
删除部分
{{ files.name }}
对应的js
removeItem() {
this.files = null
},
最终上传:
对应的js
/confirm/iUpload() {
this.$Modal./confirm/i({
title: '确定提交吗?',
onOk: () => {
this.$refs.uploadImage.post(this.files)
},
onCancel: () => {
this.$Message.warning('已取消')
},
})
},
这里我就要吹一下自己了,翻阅源码时发现iview原本的上传就是用内置的ajax的post方法实现的,这里我直接跳过了源码的封装,把post拿出来用,效果还不错~
后端后端我琢磨了挺久,一直没找到特别好的办法。主要是我原本的思路是把file作为一个完整的文件传上去,但是后来我发现COS的源码里可以接受输入流,一下子反应过来可以把文件转成InputStream再上传,一下子优雅了很多:
@ApiOperation(value = "上传图片")
@PostMapping("/uploadImage/{creator}")
public ApiReturn 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 {
@Value("${cos.bucket-name}")
private String bucketName;
@Value("${cos.bucket}")
private String bucket;
@Value("${cos.bucket-position}")
private String bucketPosition;
@Value("${cos.secret-id}")
private String secretId;
@Value("${cos.secret-key}")
private String secretKey;
@Value("${cos.app-id}")
private String appId;
private TransferManager transferManager;
private COSClient cosClient;
@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()
);
}
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));
}
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"));
}
public COSObject getCosObject(String key) {
return cosClient.getObject(bucketName, key);
}
public boolean ifImageExist(String key) {
return cosClient.doesObjectExist(bucketName, String.format("%s/%s", bucket, key));
}
public URL getUpdateFileUrl(String key) {
return cosClient.generatePresignedUrl(bucketName, String.format("%s/%s", bucket, key),
new Date(System.currentTimeMillis() + 31104 * 1000000L));
}
public void delFile(String key) {
cosClient.deleteObject(bucketName, String.format("%s/%s", bucket, key));
}
}
吐槽
腾讯COS的文档真的不大靠谱,如果你想自己写工具类,强烈建议对照源码找API,不要用文档的API。
本文首发于CSDN,作者:亭台烟雨中



