下载nginx和nginx-upload-module的gz包后进行解压,使用
./configure --prefix=/usr/local/nginx --add-module=../nginx-upload-module-2.3.0
命令添加模块,但是提示需要ssl库。
在ubuntu下执行以下命令安装opssl的开发库,再次执行configure就没有报错了。
最后执行make和make install命令完成安装。
worker_processes 20;
error_log logs/error.log notice;
working_directory /usr/local/nginx;
user root;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
server {
listen 80;
client_max_body_size 100m;
#图片
location ~ (/user/curuser/image)|(/teachers/curTeacher/course/.*/image) {
upload_pass @test;
upload_store static/images/ ;
upload_store_access user:r;
# upload_set_form_field "${upload_field_name}_name" $upload_file_name;
upload_set_form_field "${upload_field_name}_content_type" $upload_content_type;
upload_set_form_field "${upload_field_name}_path" $upload_tmp_path;
upload_aggregate_form_field "${upload_field_name}_md5" $upload_file_md5;
upload_aggregate_form_field "${upload_field_name}_size" $upload_file_size;
upload_pass_form_field "^.*$";
upload_cleanup 400 403 404 499 500-505;
}
#视频
location ~ /teachers/curTeacher/course/.*/chapter/.*/video {
upload_pass @test;
upload_store static/videos/ ;
upload_store_access user:r;
# upload_set_form_field "${upload_field_name}_name" $upload_file_name;
upload_set_form_field "${upload_field_name}_content_type" $upload_content_type;
upload_set_form_field "${upload_field_name}_path" $upload_tmp_path;
upload_aggregate_form_field "${upload_field_name}_md5" $upload_file_md5;
upload_aggregate_form_field "${upload_field_name}_size" $upload_file_size;
upload_pass_form_field "^.*$";
upload_cleanup 400 403 404 499 500-505;
}
location @test {
proxy_pass http://192.168.205.1:8091;
}
location / {
proxy_pass http://192.168.205.1:8091;
}
location /images {
root static;
}
location /videos {
internal;
root static;
}
location ~ /course/.*/chapter/.*/video {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Nginx-Proxy true;
proxy_set_header Connection "";
proxy_pass http://192.168.205.1:8091;
}
}
}
上传的图片统一放在static里的images文件夹下。
(一)上传文件后台
由于文件被nginx从请求体中分离,并且把文件在nginx服务器里的路径发送给了后端,因此后端拿到url后可以直接保存在数据库中。这里的url是在linux的绝对路径,需要自己写个函数把它变成相对路径。
@PostMapping("/user/curuser/image")
@ResponseBody
public CommonResponse uploadUserHeadImg(@RequestParam("userImageFile_path") String url) {
UserModel userModel = new UserModel();
userModel.setUserId(ShiroUtils.getCurUserId());
userModel.setHeadUrl(getRealUrl("images", url));
userService.updateUserById(userModel);
return ResponseUtils.getOKResponse();
}
(二)视频文件获取权限验证
//获取章节视频
@RequestMapping("/course/{courseId}/chapter/{chapterId}/video")
public void getChapterVideoHeader(@PathVariable("courseId") Integer courseId,
@PathVariable("chapterId") Integer chapterId,HttpServletRequest request, HttpServletResponse response) {
String chapterVideoUrl = userService.getChapterVideoUrl(ShiroUtils.getCurUserId(), courseId, chapterId);
response.setHeader("Content-Type", "application/octet-stream");
response.setHeader("X-Accel-Redirect", "/" + chapterVideoUrl);//通过这个响应头让nginx在内部获取对应的视频文件
response.setHeader("X-Accel-Charset", "utf-8");
response.setHeader("Content-Disposition", "attachment; filename=" + chapterVideoUrl);
response.setHeader("Cache-Control", "no-store"); //禁止浏览器缓存 这个可以用在试看视频上 完整视频可以缓存
}
由于用户是先上传文件,然后请求才被发到后端服务器,如果权限认证失败或者服务器入库失败,nginx应该把刚才的图片删除。上面配置文件里upload_cleanup 命令指定了后端返回什么状态码时会把文件删除,因此后端发生异常后应该返回错误码。但是由于我使用前后端分离架构,所有错误都会返回200,然后在json里描述错误原因,因此需要再定义一个异常,对这个异常返回403状态码让nginx删除图片。
UploadAdvice.java 使用AOP进行异常捕获并且转换成UploadException
@Aspect
@Component
public class UploadAdvice {
@Pointcut("execution(public * org.darod.elearning.gateway.controller.UploadImageController.*(..))")
void uploadMethod() {}
@AfterThrowing(pointcut = "uploadMethod()", throwing = "ex")
public void doUploadMethodException(Throwable ex) {
if(ex instanceof BusinessException){
BusinessException businessException = (BusinessException) ex;
throw new UploadException(businessException);
}else
throw new UploadException(EmException.UNKNOWN_ERROR,"文件上传失败");
}
}
然后在全局异常捕获中新增对UploadException的处理,让它返回403状态码。
@ExceptionHandler(UploadException.class)
@ResponseStatus(HttpStatus.FORBIDDEN)
@ResponseBody
public CommonResponse handlerUploadException(HttpServletRequest request, Exception exception) {
Map map = new HashMap<>();
if (exception instanceof UploadException) {
BusinessException businessException = (BusinessException) exception;
map.put("errCode", businessException.getErrCode());
map.put("errMsg", businessException.getErrMsg());
}
return ResponseUtils.getErrorResponse(map);
}