H5界面预览或下载文件(Minio或本地文件)
一、使用场景
最近有个老系统,需要添加图片预览和文件下载功能,文件保存在Minio上。前端需要在H5界面上,使用js获取文件信息进行图片的预览和文件下载。
先说一下两种情况,本地文件(或者通过IO流获取的文件)只能通过流方式传输到前端,然后进行展示或下载。
但是Minio上的文件,有两种方式可以处理:1.通过Minio自带的预览地址直接展示,比较方便但是会暴露Minio地址(需要安全性考虑),而且一般用于图片之类的可以直接预览的文件格式;2.读取文件流,和本地文件一样传输到前端,然后处理展示或下载,适用于类似文档表格等格式的文件。
可以先看下效果
话不多说,直接上代码
Minio上传下载相关内容就不赘述了,直接依赖就能用了。
二、Minio自带预览地址
Java代码实现
@RequestMapping("/preViewFile")
@ResponseBody
public String preViewFile(){
try {
return minioClient.getPresignedObjectUrl(Method.GET, bucketName, fileName, 60 * 60 * 24,null);
} catch (Exception e) {
e.printStackTrace();
}
}
//第一个参数是请求方式,默认给GET就行,bucketName是保存的桶名称,fileName就是Minio文件保存的地址,第四个参数是链接有效时间,最后一个请求参数,一般给Null就行。
JS代码实现
//按钮随便写一个,onclick调用onPrewViewFile()就行,这里就不赘述了!
function onPrewViewFile() {
top.$.modal.loading('文件加载中');
$.ajax({
cache: true,
type: "get",
url: prefix + "/preViewFile",
data: {},//如果有参数,可以在这里添加请求参数,get也可以改成post
async: false,
error : function(request) {
top.$.modal.closeLoading();
$.modal.alertError("系统错误");
},
success: function(data) {
window.open(data, "_blank", "width=800,height=600,left=" + (screen.width/2 - 400) + ",top=" + (screen.height/2 - 300));
top.$.modal.closeLoading();
}
});
}
//JAVA后台返回的值就是地址,所以直接使用window.open打开就可以预览了!!! "_blank"是新开窗口,再后面是窗口大小和位置,可以自定义(就是style样式)
三、文件流形式展示和下载
JAVA代码实现
@RequestMapping("/preViewFile2")
@ResponseBody
public void preViewFile2(HttpServletResponse response){
try {
//本地文件获取流方式
InputStream inputStream = new FileInputStream(new File("filePath"));
//Minio获取流方式
InputStream inputStream = minioClient.getObject(bucketName, "filePath");
//请求返回的response
ServletOutputStream outputStream = null;
try {
//设置响应头属性,第一个是附件名
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "inline;filename=" + "fileName");
//第二个允许外部客户端访问,解决跨域问题等,如果有其他要求,可自行添加
response.setHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, HttpHeaders.CONTENT_DISPOSITION);
//如果要给前端赋值类型,取消下面这行注释,fileType换成自己的文件类型即可!
//response.setHeader(HttpHeaders.CONTENT_TYPE,"filType");
//将流写入response的输出流中
outputStream = response.getOutputStream();
byte b[] = new byte[1024];
int n;
while ((n = inputStream.read(b)) != -1) {
outputStream.write(b, 0, n);
}
outputStream.flush();
} catch (Exception e) {
//有异常抛出去给外层统一处理
throw e;
} finally {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
}
}catch (Exception e){
logger.info("获取文件流报错", e);
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
//将文件流保存到response中传给前端进行处理
JS代码实现
需要注意的是,图片可以预览,但是文档类的需要其他插件才行,所以这里只做图片预览和文件下载!!!
//按钮随便写一个,onclick调用onPrewViewFile2()就行,这里就不赘述了!
function onPrewViewFile2() {
top.$.modal.loading('文件加载中');
//新建ajax请求
var xhr = new XMLHttpRequest();
//请求方式、地址和是否异步,如果是带参数,可以将参数放入最后的xhr.send(params)中即可
xhr.open("GET", prefix + "/preViewFile2", true);
//相应类型是二进制
xhr.responseType = "blob";
xhr.onload = function(e) {
//如果返回是200,正常
if (this.status == 200) {
var blob = this.response;
//fileType我原本是通过表格名称截取传进方法的,如果不通过方法传进来,则使用下面这条语句
//也可以通过let contentType = response.headers.get('Content-Type');在后端设置好后,前端获取
if (fileType == 'jpeg' ||fileType == 'jpg' || fileType == 'png') {
//如果是文件格式
var reader = new FileReader();
reader.onloadend = function () {
let image = new Image();
//文件地址赋值给image.src(文件地址在后面的reader.readAsDataURL(blob)会生成)
image.src = reader.result;
//给浏览器的请求地址,如果不给也可以预览,但是chrome浏览器左上角会有个圈圈一直转啊转,转的头晕。
//加上请求地址就好了,其他浏览器暂时没看到此问题
let url = URL.createObjectURL(blob);
//打开新的窗口,设置窗口的样式即可
let popupWindow = window.open(url, "_blank", "width=800,height=600,left=" + (screen.width / 2 - 400) + ",top=" + (screen.height / 2 - 300));
popupWindow.document.write('<html><head><title>图片预览</title></head><body style="margin: 0; padding: 0; text-align: center;">');
popupWindow.document.write('<img src="' + image.src + '" alt="Preview Image" style="max-width: 100%; max-height: 100vh;">');
popupWindow.document.write('</body></html>');
}
//将二进制文件生成对应的数据地址
reader.readAsDataURL(blob);
top.$.modal.closeLoading();
} else {
//如果是其他格式,则只允许下载
//创建一个a链接组件,用于下载
let a = document.createElement('a');
//将二进制数据创建成可下载的地址
let url = URL.createObjectURL(blob);
//赋值给a组件
a.href = url;
//fileName我原本是表格获取,如果没有,则使用下面这两行代码即可
//let contentDisposition = response.headers.get('Content-Disposition');
//let fileName = contentDisposition.split('filename=')[1];
//将文件名赋值给a.download,用于下载名
a.download = fileName;
//添加上a组件后,主动click实现下载效果。
document.body.appendChild(a);
a.click();
//释放上面创建的url对象,避免内存过大
window.URL.revokeObjectURL(url);
top.$.modal.msgSuccess("下载成功");
}
}else {
top.$.modal.closeLoading();
top.$.modal.alertError("请求错误");
}
};
//发送请求
xhr.send();
}
//注意:如果 top.$.modal.alertError("请求错误")报错,可能是缺少对应的js包,这只是个提示,可以删除掉。
总结
- Minio服务本身带有文件预览功能,我们只用获取对应地址直接打开就可以预览了,但是这会暴露Minio地址,如果对安全性有要求,不建议!
- 流形式的实现会比较复杂,需要后端获取流后传输到前端进行处理,但是对于预览和下载都可以适用,也是不错的方式。
如果需要多文件上传样式和按钮的,可以看一下另一篇文章,实现多文件的选择和删除。
🙉在小小的电脑上面敲呀敲呀敲,写短短的代码,埋小小的坑🙈
🙉在大大的电脑上面敲呀敲呀敲,写大大的代码,埋大大的坑🙈
🙉在特别大的电脑上面敲呀敲呀敲,写特别大的代码,埋特别大的坑🙈
🙉优秀的你肯定是一个不爱写Bug并且爱点赞关注的靓仔吧!🙈