背景:公司产品的文件管理本来是使用AWS的S3文件存储服务的,优点主要就是给用户上传下载的链接都是用的S3的PreSignedUrl, 可以提供一定时限内的文件访问权限,过了时限后链接就失效需要重新获取。但是现在在做一个项目不能使用S3,所以需要用其他方法来模拟这个权限操作;
需要实现以下几个API:
1.当用户请求上传的时候,发给用户一个地址,允许用户对着这个地址使用put请求传递binary的文件,该地址要在一定时间后失效
2.当用户根据文件名请求文件真实地址的时候,返回一个地址,这个地址可以直接在浏览器里面打开下载或查看(图片等),该地址要在一定时间后失效
3.Java模拟S3自己api的upload,download等操作
实现概述:
0、文件存放在nginx的目录里;
1、文件的上传是通过Java写的API来实现的;
2、文件的下载是直接访问nginx服务的URI实现的;
3、用户的上传和下载都要经过nginx并拦截非法超时请求;
4、Java模拟S3自己api的upload,download等操作实际就是Linux服务器内部的文件复制
代码:
1.文件上传API:
@RequestMapping(value = "default/{path}/{name:.+}", method = RequestMethod.PUT)
public void put(HttpServletRequest request,
@PathVariable(value = "path") String folder,
@PathVariable(value = "name") String fileName) throws Exception {
try {
InputStream inputStream = request.getInputStream();
if (inputStream != null) {
String filePath = ROOTPATH + folder + File.separator + fileName;
FileOutputStream outputStream = new FileOutputStream(new File(filePath));
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, bytesRead);
}
outputStream.flush();
}
} catch (Exception e) {
e.printStackTrace();
}
}
这个初步给到用户的上传地址就是 http://localhost/default/upload/filename.txt
API会自动解析
目录 upload/
文件名 filename.txt
然后放在Linux本地的对应路径下面去
2.文件的下载是直接访问nginx服务的URI实现的;这一步没什么好说的,就是直接访问文件http://localhost/default/upload/filename.txt
3.用户的上传和下载都要经过nginx并拦截非法超时请求;这一步是本文核心,Java的API需要先设置经过nginx反向代理转发
主要在以下几个地方加点拦截:
a.给用户上传的地址的时候不能直接给,扩展上面的http://localhost/default/upload/filename.txt,拦截后输出给用户的是
http://localhost/default/upload/filename.txt?auth=1558092039000
其中的auth参数是当前时间戳。然后写一个空文件,命名为filename.txt-1558092039000,找个nginx可以访问到的路径存放
b.给用户下载的地址同上
c.当用户来访问http://localhost/default/upload/filename.txt?auth=1558092039000的时候,会先通过nginx,在nginx里做如下配置:
#在server的外面(上面),这里主要是取到链接里的文件名
map $uri $basename {
~/(?<captured_basename>[^/]*)$ $captured_basename;
}
#在server的里面
set $auth_cache "auth_cache";
location /attachment/ {
if ($arg_auth = '') {
return 401;
}
if (!-f $document_root/$auth_cache/$basename-$arg_auth) {
return 401;
}
}
我这里是把空文件filename.txt-1558092039000放在了 nginx根目录的 /auth_cache/filename.txt-1558092039000,然后当用户来访问的时候,取用户访问的文件名和时间戳,然后去看看这个文件是否存在,如果存在的话说明是授权过的,就通过,不然就直接返回401
d.写个定时任务或者脚本,每隔一段时间去检查一遍/auth_cache/里面的文件,看那些时间戳超过了2个小时,就把这个空文件删掉,这样链接就失效了
4.略
本文中间可能省略的步骤比较多,所以有些参数名不能完整的对应。但是核心思想就是以一个空文件作为用户每次链接访问的认证。