1.controller层
@Api(tags = "文件分块上传与下载")
@RestController
@RequestMapping("/upload")
@Slf4j
public class UploadPartController {
@Autowired
private UploadPartService uploadPartService;
@ApiOperation(value = "开启分片上传")
@PostMapping("/initiateMultipartUpload")
@GlobalApiResponses
public Response initiateMultipartUpload( String filename, Integer type){
return Response.withData(uploadPartService.initiateMultipartUpload(filename,type));
}
@ApiOperation(value = "分片上传")
@PutMapping("/uploadPart")
@GlobalApiResponses
public Response uploadPart(@RequestParam("file") MultipartFile multipartFile, FileUploadPart fileUploadPart) throws IOException {
Integer partResult = uploadPartService.uploadPart(multipartFile,fileUploadPart);
return Response.withData(partResult);
}
@ApiOperation(value = "终止分片上传")
@PostMapping("/abortUpload")
@GlobalApiResponses
public Response abortUpload( FileUploadPart fileUploadPart) throws IOException {
uploadPartService.abortUpload(fileUploadPart);
return Response.withData("终止成功");
}
@ApiOperation(value = "完成分片上传")
@PostMapping("/completeUpload")
@GlobalApiResponses
public Response completeUpload( FileUploadPart fileUploadPart){
return Response.withData(uploadPartService.completeUpload(fileUploadPart));
}
@ApiOperation(value = "列出正在进行的分段上传")
@GetMapping("/listMultipartUploads")
@GlobalApiResponses
public Response listMultipartUploads(){
MultipartUploadListing multipartUploadListing = uploadPartService.listMultipartUploads();
return Response.withData(multipartUploadListing);
}
@ApiOperation(value = "列出已经上传完成的分段")
@GetMapping("/listParts")
@GlobalApiResponses
public Response listParts(String filename,String fileId){
List<Integer> uploadSuccessIndex = uploadPartService.listParts(filename, fileId);
return Response.withData(uploadSuccessIndex);
}
@ApiOperation(value = "删除文件")
@DeleteMapping("/deletePartUploads")
@GlobalApiResponses
public Response deletePartUploads(String filename){
uploadPartService.deletePartUploads(filename);
MultipartUploadListing multipartUploadListing = uploadPartService.listMultipartUploads();
return Response.withData(multipartUploadListing);
}
@ApiOperation(value = "下载文件")
@GetMapping("/download")
@GlobalApiResponses
public Response download(String filename){
return Response.withData(uploadPartService.download(filename));
}
2.service层
public interface UploadPartService {
FileInitiateDTO initiateMultipartUpload(String filename, Integer type);
Integer uploadPart(MultipartFile multipartFile, FileUploadPart fileUploadPart);
void abortUpload(FileUploadPart fileUploadPart);
CompleteMultipartUploadResult completeUpload(FileUploadPart fileUploadPart);
MultipartUploadListing listMultipartUploads();
void deletePartUploads(String filename);
List<Integer> listParts(String filename, String fileId);
String download(String filename);
Object delete(String filename);
}
3.serviceImpl实现层
@Service
public class UploadPartServiceImpl implements UploadPartService {
@Autowired
private RedisTemplate<String,Object> redisTemplate;
@Autowired
private UserUtil userUtil;
private Lock lock = new ReentrantLock();
@Value("${ceph.bucketName}")
private String bucketName;
private static AmazonS3 amazonS3 = GetConnection();
private static Map<String,Object> map = new Hashtable<>();
@Override
public FileInitiateDTO initiateMultipartUpload(String filename, Integer type) {
filename = FileCategory.getFileCategory(type).toString()+"-"+userUtil.getCurrentJWTUser().getId()+"-"+System.currentTimeMillis()+"-"+filename;
InitiateMultipartUploadRequest initiateMultipartUploadRequest = new InitiateMultipartUploadRequest(bucketName, filename);
InitiateMultipartUploadResult initiateMultipartUploadResult = amazonS3.initiateMultipartUpload(initiateMultipartUploadRequest);
return new FileInitiateDTO(initiateMultipartUploadResult.getUploadId(), filename);
}
@Override
public Integer uploadPart(MultipartFile multipartFile, FileUploadPart fileUploadPart) {
Integer uploadSuccessNumber;
byte[] md5s = null;
InputStream inputStream = null;
List<PartETag> partETags ;
try {
md5s = MessageDigest.getInstance("MD5").digest(multipartFile.getBytes());
inputStream = multipartFile.getInputStream();
} catch (NoSuchAlgorithmException | IOException e) {
throw new BusinessException("分块上传MD5加密出错");
}
UploadPartRequest uploadPartRequest = new UploadPartRequest()
.withBucketName(bucketName)
.withUploadId(fileUploadPart.getFileId())
.withKey(fileUploadPart.getFilename())
.withMD5Digest(Base64.encodeAsString(md5s))
.withPartNumber(fileUploadPart.getIndex())
.withPartSize(multipartFile.getSize())
.withInputStream(inputStream);
UploadPartResult uploadPartResult = amazonS3.uploadPart(uploadPartRequest);
lock.lock();
try{
if (redisTemplate.opsForValue().get(fileUploadPart.getFileId())==null){
redisTemplate.opsForValue().set(fileUploadPart.getFileId(),fileUploadPart.getIndex());
// partETags = new ArrayList<>();
}else {
String uploadSuccessIndex = redisTemplate.opsForValue().get(fileUploadPart.getFileId()).toString();
while(uploadSuccessIndex.indexOf(fileUploadPart.getIndex().toString())<0) {
uploadSuccessIndex += "," + fileUploadPart.getIndex();
redisTemplate.opsForValue().set(fileUploadPart.getFileId(), uploadSuccessIndex);
// partETags =(List<PartETag>)map.get(fileUploadPart.getFileId());
}
}
// partETags.add(uploadPartResult.getPartETag());
// uploadSuccessNumber = partETags.size();
// map.put(fileUploadPart.getFileId(),partETags);
redisTemplate.opsForValue().set(fileUploadPart.getFilename()+fileUploadPart.getIndex(),uploadPartResult.getETag());
uploadSuccessNumber = redisTemplate.opsForValue().get(fileUploadPart.getFileId()).toString().split(",").length;
}finally {
lock.unlock();
}
return uploadSuccessNumber;
}
@Override
public void abortUpload(FileUploadPart fileUploadPart) {
AbortMultipartUploadRequest uploadRequest = new AbortMultipartUploadRequest(bucketName,fileUploadPart.getFilename(),fileUploadPart.getFileId());
amazonS3.abortMultipartUpload(uploadRequest);
redisTemplate.delete(fileUploadPart.getFileId());
redisTemplate.delete(fileUploadPart.getFilename());
}
@Override
public CompleteMultipartUploadResult completeUpload(FileUploadPart fileUploadPart) {
// List<PartETag> partETags = (List<PartETag>) map.get(fileUploadPart.getFileId());
String[] uploadSuccessIndexs = redisTemplate.opsForValue().get(fileUploadPart.getFileId()).toString().split(",");
List<PartETag> partETags = Arrays.stream(uploadSuccessIndexs).map(s -> {
String eTag = redisTemplate.opsForValue().get(fileUploadPart.getFilename() + s).toString();
redisTemplate.delete(fileUploadPart.getFilename()+s);
return new PartETag(Integer.parseInt(s),eTag);
}).collect(Collectors.toList());
while(true) {
if (uploadSuccessIndexs.length!=0 && uploadSuccessIndexs.length == fileUploadPart.getPartTotal()) {
break;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// partETags = (List<PartETag>) map.get(fileUploadPart.getFileId());
partETags = Arrays.stream(uploadSuccessIndexs).map(s -> {
String eTag = redisTemplate.opsForValue().get(fileUploadPart.getFilename() + s).toString();
redisTemplate.delete(fileUploadPart.getFilename()+s);
return new PartETag(Integer.parseInt(s),eTag);
}).collect(Collectors.toList());
}
CompleteMultipartUploadRequest completeMultipartUploadRequest = new CompleteMultipartUploadRequest(bucketName, fileUploadPart.getFilename(), fileUploadPart.getFileId(), partETags);
CompleteMultipartUploadResult completeMultipartUploadResult = amazonS3.completeMultipartUpload(completeMultipartUploadRequest);
redisTemplate.delete(fileUploadPart.getFileId());
redisTemplate.delete(fileUploadPart.getFilename());
return completeMultipartUploadResult;
}
@Override
public MultipartUploadListing listMultipartUploads() {
ListMultipartUploadsRequest listMultipartUploadsRequest = new ListMultipartUploadsRequest(bucketName);
return amazonS3.listMultipartUploads(listMultipartUploadsRequest);
}
@Override
public void deletePartUploads(String filename) {
amazonS3.deleteObject(bucketName,filename);
}
@Override
public List<Integer> listParts(String filename, String fileId) {
ListPartsRequest listPartsRequest = new ListPartsRequest(bucketName, filename, fileId);
PartListing partListing = amazonS3.listParts(listPartsRequest);
List<PartSummary> parts = partListing.getParts();
List<Integer> uploadSuccessIndex = parts.stream().map(s -> s.getPartNumber()).collect(Collectors.toList());
return uploadSuccessIndex;
}
@Override
public String download(String filename) {
return GenerateFileURL(amazonS3,bucketName,filename);
}
@Override
public Object delete(String filename) {
return redisTemplate.delete(filename);
}
}
4.工具类
@Slf4j
@Component
public class CephOriginUtil {
private static String accessKey="你的accessKey";
private static String secretKey="你的secretKey";
private static String endpoint ="你的endpoint ";
@Value("${ceph.accessKey}")
public void setAccessKey(String accessKey) {
CephOriginUtil.accessKey = accessKey;
}
@Value("${ceph.secretKey}")
public void setSecretKey(String secretKey) {
CephOriginUtil.secretKey = secretKey;
}
@Value("${ceph.endpoint}")
public void setEndpoint(String endpoint) {
CephOriginUtil.endpoint = endpoint;
}
public static AmazonS3 GetConnection(String accessKey,String secretKey,String endpoint){
AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
AmazonS3 conn = new AmazonS3Client(credentials);
conn.setEndpoint(endpoint);
return conn;
}
public static AmazonS3 GetConnection(){
return GetConnection(accessKey, secretKey, endpoint);
}
/**
* 生成对象的下载urls
* @param conn
*/
public static String GenerateFileURL(AmazonS3 conn,String bucketName,String fileName){
GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName,fileName);
return conn.generatePresignedUrl(request).toString();
}
/**
* 删除一个对象
* @param conn
* @param bucketName
*/
public static void DeleteFile(AmazonS3 conn,String bucketName,String fileName){
conn.deleteObject(bucketName,fileName);
}
实体类(方法里没用到的属性 可以不传)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class FileUploadPart {
@ApiModelProperty(value = "当前所属文件id")
private String fileId;
@ApiModelProperty(value = "文件名称(带后缀)")
private String filename;
@ApiModelProperty(value = "切片索引")
private Integer index;
@ApiModelProperty(value = "切片数量")
private Integer partTotal;
}
配置文件(配置文件公司的 只能这样写了)
ceph:
accessKey: 你的accessKey
secretKey: 你的accessKey
endpoint: 存储的主机IP和端口 (http://ip:端口)
bucketName: 你的bucketName