1,前端调用接口实现下载 (流式下载大文件可能比较慢!也可以用直接获取下载URL的方式)
@GetMapping(value = "/downloadFile")
public void downloadFile(@RequestParam String ossPath, HttpServletResponse response) {
BufferedInputStream input;
OutputStream outputStream;
OSS ossClient = OssUtil.getOssBean();
try {
OSSObject ossObject = ossClient.getObject(OssUtil.BUCKET_NAME, ossPath);
// response.reset();
response.setCharacterEncoding("utf-8");
response.setContentType("application/x-msdownload");
response.addHeader("Content-Disposition",
"attachment;fileName=\"" + new String(ossPath.getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8)+"\"");
// "attachment;filename="+new String(ossPath.getBytes("gb2312"), "ISO8859-1"));
input = new BufferedInputStream(ossObject.getObjectContent());
byte[] buffBytes = new byte[1024];
outputStream = response.getOutputStream();
int read;
while ((read = input.read(buffBytes)) != -1) {
outputStream.write(buffBytes, 0, read);
}
outputStream.flush();
// 数据读取完成后,获取的流必须关闭,否则会造成连接泄漏,导致请求无连接可用,程序无法正常工作。
ossObject.close();
} catch (OSSException | ClientException | IOException oe) {
oe.printStackTrace();
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
}
2,简单上传接口,上传进度条可用官方api实现,这块不做实现。
@PassToken
@ApiOperation("简单上传")
@PostMapping(value = "/uploadFile")
public RestResult<Object> saveFile(HttpServletRequest request) throws IOException {
//得到上传文件夹名称
String folderName = request.getParameter("folderName");
Map<String, Object> map = new HashMap<>();
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
MultipartFile multipartFile = multipartRequest.getFile("file");
// 获取文件名
assert multipartFile != null;
String fileName = multipartFile.getOriginalFilename();
// 获取文件后缀名
assert fileName != null;
String suffixName = fileName.substring(fileName.lastIndexOf("."));
// 最后上传生成的文件名
String substring = fileName.substring(0, fileName.lastIndexOf("."));
String previewName = substring + System.currentTimeMillis() + "" + new SecureRandom().nextInt(0x0400) + suffixName;
// oss中的文件夹名
String ossPreviewName = folderName + "/" + previewName;
// 创建上传文件的元信息,可以通过文件元信息设置HTTP header(设置了才能通过返回的链接直接访问)。
ObjectMetadata objectMetadata = new ObjectMetadata();
//设置文件类型
objectMetadata.setContentType(GetFileType.getContentType(suffixName));
Date expiration = new Date(System.currentTimeMillis() + 3600L * 1000 * 24 * 365 * 50);
//文件预览
if ("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet".equals(objectMetadata.getContentType()) || "application/vnd.ms-powerpoint".equals(objectMetadata.getContentType()) || "application/msword".equals(objectMetadata.getContentType())) {
OssUtil.getOssBeanInner().putObject("你自己的bucketName", ossPreviewName, new ByteArrayInputStream(multipartFile.getBytes()), objectMetadata);
GeneratePresignedUrlRequest req = new GeneratePresignedUrlRequest("你自己的bucketName", ossPreviewName, HttpMethod.GET);
req.setExpiration(expiration);
req.setProcess(OssUtil.STYLE);
map.put("previewUrl", OssUtil.getOssBeanOut().generatePresignedUrl(req));
} else {
OssUtil.getOssBeanInner().putObject("你自己的bucketName", ossPreviewName, new ByteArrayInputStream(multipartFile.getBytes()), objectMetadata);
String previewUrl = OssUtil.getOssBeanOut().generatePresignedUrl("你自己的bucketName", ossPreviewName, expiration).toString();
map.put("previewUrl", previewUrl);
}
map.put("fileName", fileName);
map.put("ossPreviewName", ossPreviewName);
map.put("fileType", suffixName);
OssUtil.getOssBeanInner().shutdown();
OssUtil.getOssBeanOut().shutdown();
return RestResult.result(CommonResult.UPLOAD_SUCCESS, map);
}
还有一种方式获取预览链接,但是这块要开通(IMM通智能媒体预览),以及要(绑定自有域名),在OSS管理控制台中需要配置(数据处理->文档处理->文档在线预览),就能够实现文件是否支持下载,复制,打印,还能添加水印,这种形式获取文件预览url的前提是(oss库中必须有)这个文件(推荐大家用这种形式来达到文件的管控数据安全)
public static String getWebOfficeUrl(String objName) {
// 主要用于预览文件 绑定的自有域名
String endpoint = "";
// 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
// 填写Bucket名称,例如examplebucket。
String bucketName = "";
// 填写Object完整路径。Object完整路径中不能包含Bucket名称。
ClientBuilderConfiguration cf = new ClientBuilderConfiguration();
cf.setSupportCname(true);
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, "你的key", "你的秘钥", cf);
try {
// 这块就是管理控制台设置的文档水印,是否支持下载,打印。
String style = "doc/preview,print_0,copy_0,export_0/watermark,text_6Im-6Ieq5rGH,size_40,t_15,rotate_0,color_#000000";
// 指定签名URL过期时间。
Date expiration = new Date(System.currentTimeMillis() + 3600L * 1000 * 24 * 365 * 50);
GeneratePresignedUrlRequest req = new GeneratePresignedUrlRequest(bucketName, objName, HttpMethod.GET);
req.setExpiration(expiration);
req.setProcess(style);
return ossClient.generatePresignedUrl(req).toString();
} catch (OSSException oe) {
System.out.println("Caught an OSSException, which means your request made it to OSS, "
+ "but was rejected with an error response for some reason.");
System.out.println("Error Message:" + oe.getErrorMessage());
System.out.println("Error Code:" + oe.getErrorCode());
System.out.println("Request ID:" + oe.getRequestId());
System.out.println("Host ID:" + oe.getHostId());
} catch (ClientException ce) {
System.out.println("Caught an ClientException, which means the client encountered "
+ "a serious internal problem while trying to communicate with OSS, "
+ "such as not being able to access the network.");
System.out.println("Error Message:" + ce.getMessage());
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
return "";
}
3,分片上传,官方分片上传不支持进度条,我这块用的redis实现,也可用session ,大文件上传耗时前后端可能会断连导致没有上传成功的提示,所以这块最保险的做法还是websocket通信,实时告知前端进度
@PassToken
@PostMapping(value = "/multipartUpload")
public RestResult<Object> multipartUpload(HttpServletRequest request) throws Exception {
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
MultipartFile multipartFile = multipartRequest.getFile("file");
assert multipartFile != null;
String fileName = multipartFile.getOriginalFilename();
assert fileName != null;
String suffixName = fileName.substring(fileName.lastIndexOf("."));
// 创建InitiateMultipartUploadRequest对象。
InitiateMultipartUploadRequest iuRequest = new InitiateMultipartUploadRequest(OssUtil.BUCKET_NAME, "test/" + fileName);
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentType(GetFileType.getContentType(suffixName));
//设置文件类型
iuRequest.setObjectMetadata(objectMetadata);
InitiateMultipartUploadResult upresult = OssUtil.getOssBeanInner().initiateMultipartUpload(iuRequest);
// 返回uploadId,它是分片上传事件的唯一标识。您可以根据该uploadId发起相关的操作,例如取消分片上传、查询分片上传等。
String uploadId = upresult.getUploadId();
// partETags是PartETag的集合。PartETag由分片的ETag和分片号组成。
List<PartETag> partETags = new ArrayList<PartETag>();
// 每个分片的大小,用于计算文件有多少个分片。单位为字节。
final long partSize = 10 * 1024 * 1024L;
long fileLength = multipartFile.getSize();
int partCount = (int) (fileLength / partSize);
System.err.println(partCount);
if (fileLength % partSize != 0) {
partCount++;
}
// 遍历分片上传。
for (int i = 0; i < partCount; i++) {
long startPos = i * partSize;
long curPartSize = (i + 1 == partCount) ? (fileLength - startPos) : partSize;
InputStream inputStream = multipartFile.getInputStream();
// 跳过已经上传的分片。
long skip = inputStream.skip(startPos);
UploadPartRequest uploadPartRequest = new UploadPartRequest();
uploadPartRequest.setBucketName(OssUtil.BUCKET_NAME);
uploadPartRequest.setKey("test/" + fileName);
uploadPartRequest.setUploadId(uploadId);
uploadPartRequest.setInputStream(inputStream);
// 设置分片大小。除了最后一个分片没有大小限制,其他的分片最小为100 KB。
uploadPartRequest.setPartSize(curPartSize);
// 设置分片号。每一个上传的分片都有一个分片号,取值范围是1~10000,如果超出此范围,OSS将返回InvalidArgument错误码。
uploadPartRequest.setPartNumber(i + 1);
// 每个分片不需要按顺序上传,甚至可以在不同客户端上传,OSS会按照分片号排序组成完整的文件。
UploadPartResult uploadPartResult = OssUtil.getOssBeanInner().uploadPart(uploadPartRequest);
// 每次上传分片之后,OSS的返回结果包含PartETag。PartETag将被保存在partETags中。
partETags.add(uploadPartResult.getPartETag());
BigDecimal div = BigDecimalUtil.div(i * 100, partCount);
redisUtils.set("nowN", div.toString());
}
// 创建CompleteMultipartUploadRequest对象。
// 在执行完成分片上传操作时,需要提供所有有效的partETags。OSS收到提交的partETags后,会逐一验证每个分片的有效性。当所有的数据分片验证通过后,OSS将把这些分片组合成一个完整的文件。
CompleteMultipartUploadRequest completeMultipartUploadRequest =
new CompleteMultipartUploadRequest(OssUtil.BUCKET_NAME, "test/" + fileName, uploadId, partETags);
redisUtils.set("nowN", "100", 10000);
// 完成分片上传。
CompleteMultipartUploadResult completeMultipartUploadResult = OssUtil.getOssBeanInner().completeMultipartUpload(completeMultipartUploadRequest);
String previewUrl = OssUtil.getOssBeanOut().generatePresignedUrl("你自己的bucketName","test/" + fileName , new Date(System.currentTimeMillis() + 3600L * 1000 * 24 * 365 * 50)).toString();
final String location = completeMultipartUploadResult.getLocation();
OssUtil.getOssBeanInner().shutdown();
OssUtil.getOssBeanOut().shutdown();
Map<String,Object> map = new HashMap<>();
map.put("previewUrl",URLDecoder.decode(previewUrl,"UTF-8"));
return RestResult.result(CommonResult.SUCCESS, map);
}
4,工具类Ossutil
@Slf4j
public class OssUtil {
public static final String STYLE = "imm/previewdoc,copy_1";
/**
* 查看自己的节点
* (接口调用的话)内网上传 oss地址
*/
public static final String INNER_ENDPOINT = "https://oss-cn-beijing-internal.aliyuncs.com";
/**
* 查看自己的节点
* (接口调用的话)外网下载或预览 oss地址 oss-cn-beijing.aliyuncs.com
*/
public static final String OUT_ENDPOINT = "https://oss-cn-beijing.aliyuncs.com";
public static final String KEY_ID = "你自己的";
public static final String KEY_SECRET = "你自己的";
public static final String BUCKET_NAME = "你自己的";
public static void delOssFile(String fileName) {
if (fileName.isEmpty()){
return;
}
OSS oss = new OSSClientBuilder().build(OUT_ENDPOINT, KEY_ID, KEY_SECRET);
oss.deleteObject(BUCKET_NAME, fileName);
oss.shutdown();
}
public static void delOssFile(String ossPreviewName,String ossDownloadName) {
if (ossPreviewName.isEmpty()||ossDownloadName.isEmpty()){
return;
}
OSS oss = new OSSClientBuilder().build(OUT_ENDPOINT, KEY_ID, KEY_SECRET);
oss.deleteObject(BUCKET_NAME, ossPreviewName);
oss.shutdown();
}
public static OSS getOssBeanInner(){
return new OSSClientBuilder().build(INNER_ENDPOINT, KEY_ID, KEY_SECRET);
}
public static OSS getOssBeanOut(){
return new OSSClientBuilder().build(OUT_ENDPOINT, KEY_ID, KEY_SECRET);
}
public static class PutObjectProgressListener implements ProgressListener {
private HttpSession session;
private long bytesWritten = 0;
private long totalBytes = -1;
private boolean succeed = false;
private int percent = 0;
//构造方法中加入session
public PutObjectProgressListener() {
}
public PutObjectProgressListener(HttpSession mSession) {
this.session = mSession;
session.setAttribute("upload_percent", percent);
}
@Override
public void progressChanged(ProgressEvent progressEvent) {
long bytes = progressEvent.getBytes();
ProgressEventType eventType = progressEvent.getEventType();
switch (eventType) {
case TRANSFER_STARTED_EVENT:
log.info("Start to upload......");
break;
case REQUEST_CONTENT_LENGTH_EVENT:
this.totalBytes = bytes;
log.info(this.totalBytes + " bytes in total will be uploaded to OSS");
break;
case REQUEST_BYTE_TRANSFER_EVENT:
this.bytesWritten += bytes;
if (this.totalBytes != -1) {
percent = (int) (this.bytesWritten * 100.0 / this.totalBytes);
//将进度percent放入session中
session.setAttribute("upload_percent", percent);
log.info(bytes + " bytes have been written at this time, upload progress: " + percent + "%(" + this.bytesWritten + "/" + this.totalBytes + ")");
} else {
log.info(bytes + " bytes have been written at this time, upload ratio: unknown" + "(" + this.bytesWritten + "/...)");
}
break;
case TRANSFER_COMPLETED_EVENT:
this.succeed = true;
log.info("Succeed to upload, " + this.bytesWritten + " bytes have been transferred in total");
break;
case TRANSFER_FAILED_EVENT:
log.info("Failed to upload, " + this.bytesWritten + " bytes have been transferred");
break;
default:
break;
}
}
public boolean isSucceed() {
return succeed;
}
}
}