在处理项目时,经常会遇到要对图片或者视频进行上传处理,要求不仅仅是要在前端页面中可以显示,更需要后台将图片视频保存到本地的服务器中。保存图片视频其实只是单纯的java输入输出流操作,所以对学习好java语言的同学来说并不是很难。但是对于我来说就有些头疼,所以今天来说一下保存图片视频的处理。
1.图片
由于我们是后端,我们就先处理好后端的部分:
/**
* 路径工具类
* 用于获取存放文件的基础路径和相对路径
*/
public class PathUtil {
//确定该系统下的标识符"/"或"\"
//private static String seperator = System.getProperty("file.separator");
//确定系统再设置不同存放图片的基础路径
public static String getBasePath(){
//检测系统是window还是linux
String os=System.getProperty("os.name");
String basePath="";
if(os.toLowerCase().startsWith("win")){
//window系统设置路径
basePath="C:/resources";
}else {
//linux系统设置路径
basePath="/home/resources";
}
//将路径中的标识符换成对应系统标识符
//basePath=basePath.replace("/",seperator);
return basePath;
}
//确定图片存放的绝对路径(未加上文件名)
public static String getImgPath(String userId){
String imagPath="/"+userId+"/upload/images/";
//imagPath=imagPath.replace("/",seperator)
return imagPath;
}
}
上面一段代码只是一段基础的工具类,目的是实现基础路径和绝对路径的定义,将其独立出来是为了如果我们要修改这其二的路径只需要在该工具类中进行修改即可。另外,我们由于使用的服务器有些人会选择llinux有些人会选择window(现在购买服务器也可以使用window了,但大部分还是会选择linux),又或者大部分人会选择window作为开发环境进行编码,而编码完成后会上传到linux服务器。所以我在其中使用了获取system的名称来设置基础路径。
另外,原本我以为window的下划线‘ \ ’ 和linux下划线‘ / ’会影响代码运行,加入一段根据操作系统类型替换操作,但发现作用不大,所以我暂时注释掉。
还有,我是根据不同用户创建不同文件夹,所以在绝对路径函数中加入用户id的参数,是为了保证不同用户的文件存放在其目录下。
下面就是图片上传保存的工具类:
import org.apache.commons.lang.StringUtils;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
public class ImageUtil {
private static final SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
private static final Random r = new Random();
/**
* 处理图片并对图片进行本地保存
* 并返回新生成图片的相对值路径
*/
public static String saveImage(String userId,MultipartFile[] files) throws IOException {
//从pathUtil中获取图片基础路径和相对路径
String basePath = PathUtil.getImgBasePath();
String uploadPath = PathUtil.getImgPath(userId);
//输入输出流
FileOutputStream fileOutputStream = null;
InputStream inputStream = null;
try {
if (files != null && files.length > 0) {
//获取文件原本名
String fileName = files[0].getOriginalFilename();
if (StringUtils.isNotBlank(fileName)) {
// 获取不重复的随机名
String realFileName = getRandomFileName();
// 获取文件的扩展名如png,jpg等
String extension = getFileExtension(fileName);
//生成新的文件名
fileName = realFileName + extension;
//生成最终路径
String fileFinallyPath = basePath + uploadPath + fileName;
//生成相对路径,存储与数据库
uploadPath = uploadPath + fileName;
File outputFile = new File(fileFinallyPath);
makeDirPath(outputFile);
fileOutputStream = new FileOutputStream(outputFile);
inputStream = files[0].getInputStream();
IOUtils.copy(inputStream, fileOutputStream);
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//文件操作结束时关闭文件
if (fileOutputStream != null) {
fileOutputStream.flush();
fileOutputStream.close();
}
}
return uploadPath;
}
/**
* 创建目标路径所涉及到的目录
*/
private static void makeDirPath(File outputFile) {
if(outputFile.getParentFile()!=null||!outputFile.getParentFile().isDirectory()){
//如果文件夹未创建,即逐层创建
outputFile.getParentFile().mkdirs();
}
}
/**
* 获取输入文件流的扩展名,如jpg,png
*/
private static String getFileExtension(String fileName) {
return fileName.substring(fileName.lastIndexOf("."));
}
/**
* 生成随机文件名,当前年月日小时分钟秒钟+五位随机数
*/
public static String getRandomFileName() {
// 获取随机的五位数
int rannum = r.nextInt(89999) + 10000;
String nowTimeStr = sDateFormat.format(new Date());
return nowTimeStr + rannum;
}
}
该工具类要传入两个参数,一个是用户id(userId),一个是前端将图片上传后集装成的文件类型MultipartFile。而后工具类会帮忙随机生成图片名,并保存到本地中,且会将绝对路径返回(该绝对路径用于保存至数据库中)。大家可以自行参考我上面的注释进行了解。
而后就是对controller层的编写(service层中根据每个人需要编写,只需要存入图片的绝对路径即可),controller层将接口外发给前端,前端将图片上传:
@RestController
@RequestMapping("/users")
public class UsersController {
//service层接口
@Autowired
private UsersService usersService;
/**
* 用户上传头像接口,上传头像图片保存至本地
* 同时将用户信息中头像地址修改成相对地址
* @return JSONResult对象
*/
@PostMapping("/uploadimage")
public Map uploadFaceUrl(String userId,
@RequestParam("file") MultipartFile[] files )throws Exception{
Map map=new HashMap();
if(StringUtils.isBlank(userId)){
map.put("error",true);
map.put("msg","用户Id不能为空...");
return map;
}
String uploadPathDB=null;
try {
uploadPathDB=ImageUtil.generateThumbnail(userId,files);
}catch (IOException e){
e.printStackTrace();
map.put("error",true);
map.put("msg","头像上传出错...");
return map;
}
//更新用户中头像地址
Users user=new Users();
user.setId(userId);
user.setAvatarUrl(uploadPathDB);
usersService.updateUsers(user);
map.put("error",false);
map.put("msg",uploadPathDB);
return map;
}
}
将前端传入的参数导入ImageUtil工具类中即可,返回的绝对路径存放入数据库中,并将绝对路径返回到前端。
最后还要进行配置:
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfigure implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
String basePath=PathUtil.getBasePath();
String fileBasePath="file:"+basePath+"/";
registry.addResourceHandler("/**").
//保证其他资源不会被屏蔽
addResourceLocations("classpath:/META-INF/resources/").
//设置入我们的基础路径
addResourceLocations(fileBasePath);
}
}
因为我们传入前端的是绝对路径,所以我们在配置类中将基础路径设置进来,而要访问图片就无需将基础路径写入,使用绝对路径就可以访问到图片了。
下面就是访问图片了,我们可以直接在浏览器中输入下面url进行测试:
服务器地址+图片绝对路径
如:我的服务器(内网)地址是:http://10.242.65.196:8080;
图片的相对路径是:/190227GKB8YBWHM8/upload/images/2019030521040350813.jpg
在浏览器中输入:http://10.242.65.196:8080/190227GKB8YBWHM8/upload/images/2019030521040350813.jpg
2.音频
音频和视频一样,都是差不多的过程,我这里就再重复贴一次代码:
首先仍是路径工具类PathUtil(和上面一样,就是加了个寻找视频相对路径函数):
/**
* 路径工具类
* 用于获取存放文件的基础路径和相对路径
*/
public class PathUtil {
//确定该系统下的标识符"/"或"\"
//private static String seperator = System.getProperty("file.separator");
//确定系统再设置不同存放图片的基础路径
public static String getBasePath(){
//检测系统是window还是linux
String os=System.getProperty("os.name");
String basePath="";
if(os.toLowerCase().startsWith("win")){
//window系统设置路径
basePath="C:/resources";
}else {
//linux系统设置路径
basePath="/home/resources";
}
//将路径中的标识符换成对应系统标识符
//basePath=basePath.replace("/",seperator);
return basePath;
}
//确定图片存放的绝对路径(未加上文件名)
public static String getImgPath(String userId){
String imagPath="/"+userId+"/upload/images/";
//imagPath=imagPath.replace("/",seperator)
return imagPath;
}
//确定音频存放的绝对路径(未加上文件名)
public static String getVideoPath(String userId){
String videoPath="/"+userId+"/upload/video/";
//videoPath=videoPath.replace("/",seperator)
return videoPath;
}
}
然后就是保存音频的工具类(与上面也类似,只是改为了单个文件上传):
import org.apache.commons.lang.StringUtils;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
public class VideoUtil {
private static final SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
private static final Random r = new Random();
/**
* 处理视频并保存视频
* 并返回新生成图片的相对值路径
*/
public static String saveVideo(String userId,MultipartFile file) throws IOException {
//从pathUtil中获取图片基础路径和相对路径
String basePath = PathUtil.getBasePath();
String uploadPath = PathUtil.getVideoPath(userId);
//输入输出流
FileOutputStream fileOutputStream = null;
InputStream inputStream = null;
try {
if (file != null ) {
//获取文件原本名
String fileName = file.getOriginalFilename();
if (StringUtils.isNotBlank(fileName)) {
// 获取不重复的随机名
String realFileName = getRandomFileName();
// 获取文件的扩展名如png,jpg等
String extension = getFileExtension(fileName);
//生成新的文件名
fileName = realFileName + extension;
//生成最终路径
String fileFinallyPath = basePath + uploadPath + fileName;
//生成相对路径,存储与数据库
uploadPath = uploadPath + fileName;
File outputFile = new File(fileFinallyPath);
makeDirPath(outputFile);
fileOutputStream = new FileOutputStream(outputFile);
inputStream = file.getInputStream();
IOUtils.copy(inputStream, fileOutputStream);
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//文件操作结束时关闭文件
if (fileOutputStream != null) {
fileOutputStream.flush();
fileOutputStream.close();
}
}
return uploadPath;
}
/**
* 创建目标路径所涉及到的目录
*/
private static void makeDirPath(File outputFile) {
if(outputFile.getParentFile()!=null||!outputFile.getParentFile().isDirectory()){
//如果文件夹未创建,即逐层创建
outputFile.getParentFile().mkdirs();
}
}
/**
* 获取输入文件流的扩展名,如.mp4,.mp3, .avi
*/
public static String getFileExtension(String fileName) {
return fileName.substring(fileName.lastIndexOf("."));
}
/**
* 生成随机文件名,当前年月日小时分钟秒钟+五位随机数
*/
public static String getRandomFileName() {
// 获取随机的五位数
int rannum = r.nextInt(89999) + 10000;
String nowTimeStr = sDateFormat.format(new Date());
return nowTimeStr + rannum;
}
}
接下来就是controller层的编写(依旧默认service层已经写好):
@RestController
@RequestMapping("/video")
public class VideoController {
@Autowired
private BgmService bgmService;
@Autowired
private VideoService videoService;
/**
* 用户上传视频接口,上传视频保存至本地
* 并同时传入多个参数
* 至video对象
* @return JSONResult对象
*/
@PostMapping(value = "/uploadvideo",headers = "content-type=multipart/form-data")
public Map uploadVideo(String userId, ....,
@RequestParam("file") MultipartFile file )throws Exception{
Map map=new HashMap();
if(StringUtils.isBlank(userId)){
map.put("error",true);
map.put("msg","用户Id不能为空...");
return map;
}
String uploadPathDB=null;
try {
uploadPathDB=VideoUtil.saveVideo(userId,file);
}catch (IOException e) {
e.printStackTrace();
map.put("error",true);
map.put("msg","视频上传出错");
return map;;
}
//新建对象
Videos video=new Videos();
video.setUserId(userId);
//设置视频的相对路径
video.setVideoPath(uploadPathDB);
//保存其他从前端传入的参数
video.set....(...);
//保存到数据库
videoService.saveVideo(video);
map.put("error",false);
map.put("msg",uploadPathDB);
return map;
}
}
最后,还是要进行配置:
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfigure implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
String basePath=PathUtil.getBasePath();
String fileBasePath="file:"+basePath+"/";
registry.addResourceHandler("/**").
//保证其他资源不会被屏蔽
addResourceLocations("classpath:/META-INF/resources/").
//设置入我们的基础路径
addResourceLocations(fileBasePath);
}
}
我们依旧访问服务器地址+音频绝对路径即可访问服务器
**补充:音频上传超限报错
错误:org.apache.tomcat.util.http.fileupload.FileUploadBase$FileSizeLimitExceededException: The field file exceeds its maximum permitted size of 1048576 bytes
出错原因:这是因为spring有限制最大传输为10MB,我们要调整其中配置
解决方法:
首先在配置文件(application.properties)中配置:
#设置文件上传最大容量
spring.servlet.multipart.enabled=true
spring.servlet.multipart.maxFileSize = 100000000
spring.servlet.multipart.maxRequestSize=100000000
然后在启动类中加入配置(记得加上@Configuration):
@SpringBootApplication
@Configuration
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(VideoApplication.class, args);
}
/**
* 修改文件上传容量
*/
@Bean
public MultipartConfigElement multipartConfigElement() {
MultipartConfigFactory factory = new MultipartConfigFactory();
// 单个数据大小
//factory.setMaxFileSize("10240KB"); // KB,MB
/// 总上传数据大小
//factory.setMaxRequestSize("102400KB");
DataSize dataSize=null;
dataSize=dataSize.ofBytes(100000000);
factory.setMaxFileSize(dataSize);
factory.setMaxRequestSize(dataSize);
return factory.createMultipartConfig();
}
}
这是最新版本(spring boot2.1)的配置,我发现网上并没有人说明,他们都是使用旧的方法,特此记录一下。大家如果用新版本的话会发现配置文件和factory.setMaxFileSize("10240KB")都是无法使用的,要用新配置文件写法和DataSize类来进行编写。
**补充2:临时文件夹失效被删
问题:Caused by: java.io.IOException: The temporary upload location [/tmp/tomcat.**/work/Tomcat/localhost/ROOT] is not valid
由于临时文件夹被系统删除导致失效,所以会报错
只需要在启动类中加入临时创建的文件的配置bean:
/**
* 修改文件上传存放的临时路径
* 修改文件上传容量
*/
@Bean
public MultipartConfigElement multipartConfigElement() {
MultipartConfigFactory factory = new MultipartConfigFactory();
//设置文件上传临时文件的位置,以防出错存储
String location = PathUtil.getTemporaryPath();
factory.setLocation(location);
return factory.createMultipartConfig();
}
其中PathUtil中添加临时文件路径的函数:
/**
* 路径工具类
* 用于获取存放文件的基础路径和相对路径
*/
public class PathUtil {
//确定系统再设置不同存放图片的基础路径
public static String getTemporaryPath(){
//检测系统是window还是linux
String os=System.getProperty("os.name");
String basePath="";
if(os.toLowerCase().startsWith("win")){
//window系统设置路径
basePath="C:\\video-resources\\data\\tmp";
}else {
//linux系统设置路径
basePath="/home/video-resources/data/tmp";
}
//将路径中的标识符换成对应系统标识符
//basePath=basePath.replace("/",seperator);
File file=new File(basePath);
if(file.getParentFile()!=null||!file.getParentFile().isDirectory()){
//如果文件夹未创建,即逐层创建
file.getParentFile().mkdirs();
}
return basePath;
}
}