Java后端OSS文件上传下载批量下载删除以及部分前端代码

最近遇到一个功能要求从阿里云OSS服务器下载文件并批量导出,从网上找到的文章内容与现在阿里云文档操作手册有部分出入,导致拿取文件出问题,综合了大部分文章才完成功能,所以想记录一下以免下次用的时候忘记了
首先就是工具类中要连接OSS的Client
参考的文章如下:
前端批量下载请求报错处理
对返回的zip流以及blob格式的res处理
后端借鉴文章
阿里云OSS流式下载操作手册

插入maven依赖

		<dependency>
			<groupId>com.aliyun.oss</groupId>
			<artifactId>aliyun-sdk-oss</artifactId>
			<version>3.8.0</version>
		</dependency>

这里我的配置信息是放在配置文件中的,看项目情况 也可以放到Nacos或者数据库里

oss.host: https://oss-cn-beijing.aliyuncs.com(阿里云域名)
oss.bucketName: tests-xxxxxxx-----(oss创建的服务名)
oss.endpoint: oss-cn-beijing.aliyuncs.com(阿里云域名)
oss.accessKeyId: LTAxxxxxxxxxxxxxxxxx(这里的IDSecret需要在阿里云的RAM)
oss.accessKeySecret: 1QInP3xxxxxxxxxxxxxxxxxxx

访问控制里创建一个你自己的用户,生成自己的秘钥对,如下图
在这里插入图片描述

@Data
@Component
public class OssUtil {
	//读取OSS服务配置信息
	private String host = AppConfig.getProperty("oss.host");
	private String endpoint = AppConfig.getProperty("oss.endpoint");
	private String accessKeyId = AppConfig.getProperty("oss.accessKeyId");
	private String accessKeySecret = AppConfig.getProperty("oss.accessKeySecret");
	private String bucketName = AppConfig.getProperty("oss.bucketName");
	/**
	 * 上传文件
	 * @param file
	 * @return
	 */
	public HashMap<String, Object> upload(MultipartFile file,String path) {
		if (file.isEmpty()) {
			throw new ServiceException("上传文件不能为空");
		}
		if (!checkFileSize(file.getSize(), BaseConstant.LEN, BaseConstant.UNIT_M)) {
			throw new ServiceException("上传文件大小不能超过10M");
		}
		HashMap<String, Object> resultMap = new HashMap<>();
		// 创建OSSClient实例。
		OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

		// 上传文件流。
		// 保存路径,注意:这里不要以 / 或 \ 开头
		path += "/"+new SimpleDateFormat("yyyyMMdd").format(new Date());
		String originalFilename = file.getOriginalFilename();
		String uuidName = UUID.randomUUID().toString().replace("-", "");
		String fileName = path + "/" + uuidName + originalFilename.substring(originalFilename.lastIndexOf("."));
		resultMap.put("path",path);
		resultMap.put("fileName",uuidName+originalFilename.substring(originalFilename.lastIndexOf(".")));
		try {
			ossClient.putObject(bucketName, fileName, file.getInputStream());
		} catch (IOException e) {
			throw new ServiceException("上传失败" + e.getMessage());
		}
		// 关闭OSSClient。
		ossClient.shutdown();
		return resultMap;
	}

	/**
	 * 删除文件
	 * @param url 示例:'download/file.xsl' oss服务器文件路径以及文件名
	 */
	public void remove(String url) {

		try {
			// 创建OSSClient实例。
			OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
			// 判断当前文件url 是否存在
			boolean exist = ossClient.doesObjectExist(bucketName, url);
			if (!exist) {
				System.out.println("文件不存在");
			} else {
				// 删除文件。
				ossClient.deleteObject(bucketName, url);
				// 关闭OSSClient。
				ossClient.shutdown();

			}
		} catch (Exception e) {
			System.out.println(e);
		}

	}
	/**
	 * 下载文件,此处主要是给Controller层提供一个OSS的Object类
	 * objectName示例:download/file.xsl,()这里是oss文件列表中 文件路径和文件名
	 */
	public OSSObject downloadOssFile(OutputStream os, String objectName) throws IOException {
		// ossObject包含文件所在的存储空间名称、文件名称、文件元信息以及一个输入流。
		OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
		OSSObject ossObject = ossClient.getObject(bucketName, objectName);
		return ossObject;

	}
	/**
	 * 校验文件
	 * @param len
	 * @param size
	 * @param unit
	 * @return
	 */
	public boolean checkFileSize(Long len, int size, String unit) {
		double fileSize = 0;
		if (BaseConstant.UNIT_B.equals(unit.toUpperCase())) {
			fileSize = (double) len;
		} else if (BaseConstant.UNIT_K.equals(unit.toUpperCase())) {
			fileSize = (double) len / 1024;
		} else if (BaseConstant.UNIT_M.equals(unit.toUpperCase())) {
			fileSize = (double) len / 1048576;
		} else if (BaseConstant.UNIT_G.equals(unit.toUpperCase())) {
			fileSize = (double) len / 1073741824;
		}
		if (fileSize > size) {
			return false;
		}
		return true;
	}

}

文件常量类,在工具类中有使用到,初步了解应该是用来限制文件的

public class BaseConstant {

	public static final Integer NO = 0;

	public static final Integer YES = 1;

	public static final int LEN = 10;

	public static final String UNIT_M = "M";

	public static final String UNIT_B = "B";

	public static final String UNIT_K = "K";

	public static final String UNIT_G = "G";
}

以上便是工具类的创建,下面是前后端上传代码
前端代码

			<q-dialog ref="addFileModal" width="30%" height="300" title="新增附件" :showFooter="true">
				<el-form ref="addfileForm" :model="addfileForm" label-width="90px" class="searForm" size="mini">
					<el-form-item label="阶段名称:">
						<el-select v-model="addfileForm.jdid" filterable placeholder="请选择" style="width: 100%;">
							<el-option v-for="item in jdNameList" :key="item.JDID" :label="item.JDMC" :value="item.JDID" />
						</el-select>
					</el-form-item>
					<el-form-item label="上传附件:">
						<el-upload
							class="upload-demo"
							action
							:http-request="importMxData"
							:on-preview="handlePreview"
							:on-remove="handleRemove"
							:before-remove="beforeRemove"
							multiple
							:limit="3"
							:on-exceed="handleExceed"
							:file-list="fileList">
							<el-button size="small" type="primary">上传附件</el-button>
						</el-upload>
					</el-form-item>
					<el-form-item label="备注:">
						<el-input type="textarea" v-model="addfileForm.bz" placeholder="请输入备注"></el-input>
					</el-form-item>
				</el-form>
				<div slot="footer">
					<q-button type="primary" @click="saveFileJd()">确定</q-button>
					<q-button @click="addFileClose">取消</q-button>
				</div>
			</q-dialog>

下载的HTMl代码就不贴了,只要一个button能触发此方法就可以了

			import request from '@/utils/request'
		    //下载请求
		    downRequest: function (url, data) {
		        return request({
		            url: contextPath + url,
		            method: 'get',
		            params: data,
		            responseType: 'blob',//设置返回类型
		            headers: {'Content-Type': 'application/json; application/octet-stream'}//设置请求头
		        })
		    }
			import http from '../index'
			export function downAllOssFile(params) {
			    return http.downRequest('xxxxxxxxxxxx/xxxxxxxxxxxx',params)
			}

上传需要将文件转换为FormData格式

			data(){
				return{
					FileForm:''
				}
			}
			//处理附件
			importMxData(item){
				let FormDatas = new FormData()
				FormDatas.append('file',item.file);
				FormDatas.append('time',1);
				FormDatas.append('epath','sanss');
				this.FileForm = FormDatas
			},
			//上传附件
			saveFileJd(){
				//追加参数
				this.FileForm.append('jdid',this.addfileForm.jdid);
				//调用方法,这里前端大哥封装的调接口方法直接传FileForm数组
				insetStageFile(this.FileForm).then(res => {
				if (res.code === 0) {
					//关闭弹窗
					this.addFileClose()
				} else {
					this.$message.error(res.msg)
					this.addFileClose()
				}
				}).catch(error => {
					this.addFileClose()
				})
			},
			//批量下载-----------------------------------
			bulkDownload(){
				 downAllOssFile({
					filename:'fujian/fujian1.xls,fujian/fujian2.xls'
				 }).then(res => {
					const content = res
					const blob = new Blob([content])
					const fileName = 'LOG.zip'
					if ('download' in document.createElement('a')) { // 非IE下载
						const elink = document.createElement('a')
						elink.download = fileName
						elink.style.display = 'none'
						elink.href = URL.createObjectURL(blob)
						document.body.appendChild(elink)
						elink.click()
						URL.revokeObjectURL(elink.href) // 释放URL 对象
						document.body.removeChild(elink)
					} else { // IE10+下载
						navigator.msSaveBlob(blob, fileName)
					}
				 }).catch(error => {

				 })
			},

控制层代码

	/**
	 *  上传文件到OSS
	 * @return
	 */
	@PostMapping("/upload")
	public R upload(@RequestParam("file") MultipartFile file) {
		//文件夹名称
		String test = "muban";
		return new R(ossUtil.upload(file,test));
	}
	
	/**
	 *  项目资料列表 点击删除附件
	 * @return
	 */
	@GetMapping("/deleteFile")
	@ResponseBody
	public R deleteFile(@RequestParam("epath") String epath,@RequestParam("ename") String ename) {
		//前端传过来文件路径以及文件名,路径以及文件名要做加密解密,这里只做示例展示
		ossUtil.remove(epath +"/"+ ename);
		return R.success();
	}
	/***
	 * 批量下载(zip)
	 * @param objectName
	 * @param response
	 * @throws IOException
	 * objectName为前端传过来的多个文件路径字符串,用逗号隔开('filePath/file.xsl','filePath/file1.xsl','filePath/file2.xsl')
	 */
	@RequestMapping("downAllOssFile")
	@ResponseBody
	public void downAllOssFile(@RequestParam("objectName") String objectName,HttpServletRequest request, HttpServletResponse response) throws IOException {
		try {
			String momentFileName = "全部资源下载.zip";
			// 创建临时文件
			File zipFile = File.createTempFile("批量下载", ".zip");
			FileOutputStream f = new FileOutputStream(zipFile);
			CheckedOutputStream csum = new CheckedOutputStream(f, new Adler32());
			// 用于将数据压缩成Zip文件格式
			ZipOutputStream zos = new ZipOutputStream(csum);
			String[] files = objectName.split(",");
			for (String string : files) {
				String fName = string.trim();
				String eachFileName = fName.substring(fName.lastIndexOf("/")+1);
				String tmp = fName.substring(0,fName.lastIndexOf("/"));
				tmp = tmp.substring(tmp.lastIndexOf("/")+1);
				String fileName = tmp + "/" + eachFileName;
				OSSObject ossObject =  ossUtil.downloadOssFile(response.getOutputStream(), fileName);
				InputStream inputStream = ossObject.getObjectContent();
				zos.putNextEntry(new ZipEntry(eachFileName));
				int bytesRead;
				// 向压缩文件中输出数据
				while((bytesRead = inputStream.read())!=-1){
					zos.write(bytesRead);
				}
				inputStream.close();
				zos.closeEntry();
			}
			zos.close();
			String header = request.getHeader("User-Agent").toUpperCase();
			if (header.contains("MSIE") || header.contains("TRIDENT") || header.contains("EDGE")) {
				momentFileName = URLEncoder.encode(momentFileName, "utf-8");
				momentFileName = momentFileName.replace("+", "%20");    //IE下载文件名空格变+号问题
			} else {
				momentFileName = new String(momentFileName.getBytes(), "ISO8859-1");
			}
			response.reset();
			response.setContentType("text/plain");
			response.setContentType("application/octet-stream; charset=utf-8");
			response.setHeader("Location", momentFileName);
			response.setHeader("Cache-Control", "max-age=0");
			response.setHeader("Content-Disposition", "attachment; filename=" + momentFileName);
			FileInputStream fis = new FileInputStream(zipFile);
			BufferedInputStream buff = new BufferedInputStream(fis);
			BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
			byte[] car = new byte[1024];
			int l=0;
			while (l < zipFile.length()) {
				int j = buff.read(car, 0, 1024);
				l += j;
				out.write(car, 0, j);
			}
			// 关闭流
			fis.close();
			buff.close();
			out.close();
			// 删除临时文件
			zipFile.delete();
		} catch (Exception e) {
			e.printStackTrace();
		}

	}
  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
前端传file文件可以使用HTML5的FormData对象实现,其中包含一个File类型的字段。后端实现断点上传oss代码可以参考以下示例代码: ```python import oss2 import os from flask import Flask, request app = Flask(__name__) # 初始化OSS客户端 auth = oss2.Auth('<AccessKeyId>', '<AccessKeySecret>') bucket = oss2.Bucket(auth, '<Endpoint>', '<BucketName>') @app.route('/upload', methods=['POST']) def upload(): file = request.files.get('file') chunk_size = 1024 * 1024 # 每个分片的大小,这里设置为1MB object_name = file.filename # 对象名称,这里使用文件名作为对象名 # 判断是否有上传记录,如果有则继续上传,如果没有则从头开始上传 if 'upload_id' in request.args and 'part_number' in request.args: upload_id = request.args.get('upload_id') part_number = int(request.args.get('part_number')) part = request.data bucket.upload_part(object_name, upload_id, part_number, part) return 'Part uploaded successfully' # 初始化分片上传 upload_id = bucket.init_multipart_upload(object_name).upload_id # 上传所有分片 parts = [] part_number = 1 while True: part = file.read(chunk_size) if not part: break bucket.upload_part(object_name, upload_id, part_number, part) parts.append(oss2.models.PartInfo(part_number, oss2.utils.calc_md5(part, len(part)))) part_number += 1 # 完成分片上传 bucket.complete_multipart_upload(object_name, upload_id, parts) return 'File uploaded successfully' if __name__ == '__main__': app.run() ``` 上述代码使用Flask框架实现了一个上传接口,前端可以通过POST请求将文件上传到该接口。后端文件分成多个分片,然后逐个上传到OSS,并记录上传记录。如果上传过程中出现中断,可以利用上传记录继续上传未上传完成的分片。最后完成所有分片上传后,调用`complete_multipart_upload`方法完成分片上传。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值