在工作中或多或少会遇见关于文件上传的功能,最近在做的项目中就涉及到了大量的文件上传,包括图片和视频等,话不多说,直接开整!!!
文章目录
- base64文件上传
- 多图片上传
- 视频上传
- 简单介绍一下ffmpeg:
- ffmpeg安装
- 使用ffmpeg进行压缩文件
- 文件复制
- 视频上传整合代码
- 七牛云文件上传
- 七牛云截图
- 总结
base64文件上传
首先,先给大家贴一个工具类,是关于使用base64上传的:
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Base64;
public class FileUtil {
//文件转换base64
public static String fileToBase64(String path) {
String base64 = null;
InputStream in = null;
try {
File file = new File(path);
in = new FileInputStream(file);
byte[] bytes = new byte[(int) file.length()];
in.read(bytes);
base64 = Base64.getEncoder().encodeToString(bytes);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return base64;
}
//base64转文件
public static void base64ToFile(String base64, String destPath, String fileName) throws IOException {
File file = null;
//创建文件目录
String filePath = destPath;
File dir = new File(filePath);
if (!dir.exists() && !dir.isDirectory()) {
dir.mkdirs();
}
BufferedOutputStream bos = null;
java.io.FileOutputStream fos = null;
try {
byte[] bytes = Base64.getDecoder().decode(base64);
file = new File(filePath + "/" + fileName);
fos = new java.io.FileOutputStream(file);
bos = new BufferedOutputStream(fos);
bos.write(bytes);
} finally {
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
关于base64我本次项目中只使用了一次就更换了其他方式,所以今天主要推荐的是其他方式的上传操作:
多图片上传
多图片上传使用的是MultipartFile,关于MultipartFile一般用来接收前端传过来的文件,这里不做太多解释
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class FileUtils {
//MultipartFile[]多文件上传
public static List<String> upload(String userId, MultipartFile[] files) {
List<String> fileNameList = new ArrayList<>();
try {
for (int i = 0; i < files.length; i++) {
//获取原文件名
String originalFileName = files[i].getOriginalFilename();
//获取文件后缀名
String suffix = originalFileName.substring(originalFileName.lastIndexOf("."));
//设置文件名
String fileName = System.currentTimeMillis() + suffix;
//设置文件存储路径
String filePath = "换成本地路径比如/root/abc/" + fileName;
File dest = new File(filePath);
// 检测是否存在目录
if (!dest.getParentFile().exists()) {
dest.getParentFile().mkdirs();// 新建文件夹
}
files[i].transferTo(dest);// 文件写入
fileNameList.add(fileName);
}
} catch (IOException e) {
e.printStackTrace();
}
return fileNameList;
}
}
工具类写好了,那么来编写controller:
这里是做的一个广告位多图片上传的操作
@PostMapping("/upload")
public String upload(Advert advert, @RequestParam("file") MultipartFile[] files) {
if (files == null) {
return "文件不存在";
}
//调用工具类进行上传操作
advert.setFileName(FileUtils.upload(advert.getUserId(), files));
//调用service进行逻辑操作
advertService.uploadAdvertImage(advert);
if (advert != null) {
return "上传成功";
}
return "上传失败";
}
servce层大概就不用展示了吧,就是对数据进行操作的部分,下面来继续其他内容
视频上传
其实视频上传也是一样的文件上传,但是由于我们使用的App手机拍摄的视频很大,十几秒就可以达到数百兆的文件,所以需要对前端传过来的文件进行压缩处理,这里使用的ffmpeg来进行视频压缩:
简单介绍一下ffmpeg:
ffmpeg是一套可以进行音视频转换开源程序,使用需要下载软件和相应的jar包,
ffmpeg安装
https://ffmpeg.org/download.html#build-linux 首先打开官方网站的下载链接,选择linux系统
或者直接访问:linux系统安装ffmpeg
推荐安装最新版本,下载完成后去linux系统进行解压操作,先复制进linux系统,然后找到文件路径
执行xz -d ffmpeg-...........tar.xz
然后去掉了xz后再进行一次解压tar -xvf ffmpeg-............tar
解压完成后进入文件目录执行./ffmpeg
即可查看是否安装成功
接下来就可以愉快的进行压缩操作了!!!
使用ffmpeg进行压缩文件
亲测100M的视频可以压缩成不到2M,并且基本看不出太大区别,流畅度也没有进行变化,下面先贴代码:
/**
* 压缩视频
*
* @param convertFile 待转换的文件
* @param targetFile 转换后的目标文件
*/
private static void toCompressFile(String convertFile, String targetFile) {
try {
Runtime runtime = Runtime.getRuntime();
/**将视频压缩为 每秒15帧 平均码率600k 画面的宽与高 为1280*720*/
String cutCmd = "ffmpeg -i " + convertFile + " -r 15 -b:v 600k -s 1280x720 " + targetFile;
runtime.exec(cutCmd);
System.out.println("文件:" + convertFile + " 正在转换中。。。");
} catch (Exception e) {
e.printStackTrace();
System.out.println("压缩文件出现异常:" + e.getMessage());
}
}
可以直接使用下图代码进行测试:
public static void main(String[] ars ){
//因系统权限问题,注意使用C路径的话可能找不到文件,这里不多说
final String convertFile="本地路径";
File f=new File(convertFile);
File[] fs=f.listFiles();
for(File ff:fs){
if(ff.toPath().toString().toLowerCase().endsWith(".mp4")){
String f1=convertFile+ff.getName();
String f2=convertFile+"ys"+ff.getName();
toCompressFile(f1,f2);
}
}
}
那么文件压缩好了,如何去保存到服务器上呢,这里我们临时文件存放的路径和保存到服务器上的路径是不一样的,所以这里使用到了文件复制操作:
文件复制
/**
* 复制文件
*
* @param filePath 被复制的文件路径
* @param toFilePath 粘贴的路径
*/
public static void copyFile(String filePath, String toFilePath) {
try {
//给被复制文件的路径,创建一个文件
File file = new File(filePath);
//创建粘贴的文件路径
File copyFile = new File(toFilePath);
//新建一个文件
copyFile.createNewFile();
//创建输入、输出流
FileInputStream fis = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(copyFile);
//定义一个byte数组
byte[] bytes = new byte[512];
int len = 0;
//判断有没有读取完成
while ((len = fis.read(bytes)) != -1) {
//写入数据
fos.write(bytes, 0, len);
}
//释放资源
fis.close();
fos.flush();
fos.close();
//复制完成
} catch (IOException e) {
e.printStackTrace();
}
}
文件复制工具类做好了,那么我们需要把原文件删除,总不能把几百兆的视频一直保存吧?于是又需要使用删除文件的代码:
//删除压缩前后的文件
//取得当前主机存放项目的绝对路径
String workPath = f1;
String beforeWorkPath = f2;
//删除文件
File deleteFile = new File(workPath);
File deleteBeforeFile = new File(beforeWorkPath);
if (deleteFile.exists() && deleteFile.isFile()
&& deleteFile.delete() == true && deleteBeforeFile.exists() && deleteBeforeFile.isFile()
&& deleteBeforeFile.delete() == true) {
//上面有其他代码,这里代表删除成功,自定义返回!!!其他代码在后面会贴上
hashMap.put("msg", "删除文件成功");
}
这样一系列的操作就完成了,那么来看一下已经整合好的视频上传代码:
视频上传整合代码
@PostMapping(value = "/videoUpload/{userId}")
public HashMap diplomaFrontUpload(@PathVariable String userId, String describe, String classId, String videoAddress, @RequestParam("file") MultipartFile file) {
HashMap hashMap = new HashMap();
String originalFileName = file.getOriginalFilename();
String suffix = originalFileName.substring(originalFileName.lastIndexOf("."));
try {
if (file.isEmpty()) {
hashMap.put("msg", "文件不存在");
return hashMap;
}
// 获取文件名
String fileName = "v" + System.currentTimeMillis() + suffix;
// 设置文件存储路径
String filePath = "本地路径" + fileName;
//String filePath = "C:\\uddemo\\" + userId + "\\" + fileName;
File dest = new File(filePath);
// 检测是否存在目录
if (!dest.getParentFile().exists()) {
dest.getParentFile().mkdirs();// 新建文件夹
}
file.transferTo(dest);// 文件写入
//压缩操作
//convertFile 待转换的文件路径
//targetFile 转换后的目标文件路径
final String convertFile = "本地路径";
File f = new File(convertFile);
File[] fs = f.listFiles();
String f1 = null;
String f2 = null;
String ysFileName = null;
for (File ff : fs) {
if (ff.toPath().toString().toLowerCase().endsWith(".mp4")) {
f1 = convertFile + ff.getName();
f2 = convertFile + "ys" + ff.getName();
ysFileName = "ys" + ff.getName();
toCompressFile(f1, f2);
}
}
//复制压缩后文件到可访问路径
copyFile(f2, "本地路径" + ysFileName);
//删除压缩前后的文件
//取得当前主机存放项目的绝对路径
String workPath = f1;
String beforeWorkPath = f2;
//删除文件
File deleteFile = new File(workPath);
File deleteBeforeFile = new File(beforeWorkPath);
if (deleteFile.exists() && deleteFile.isFile()
&& deleteFile.delete() == true && deleteBeforeFile.exists() && deleteBeforeFile.isFile()
&& deleteBeforeFile.delete() == true) {
hashMap.put("msg", "删除文件成功");
}
//调用service进行逻辑操作,代码不展示
hashMap.put("content", videoService.upload(userId, ysFileName, describe, classId, videoAddress));
hashMap.put("msg", "上传成功!");
return hashMap;
} catch (
IllegalStateException e) {
e.printStackTrace();
} catch (
IOException e) {
e.printStackTrace();
}
hashMap.put("msg", "上传失败");
return hashMap;
}
至此,视频上传的代码就整合好了,如有错误可留言给我,谢谢
中途也使用了七牛云来进行文件上传,那么我们也来看一下七牛云是如何使用的:
七牛云文件上传
首先我们需要注册七牛云,然后我们这边使用的是对象存储功能,关于下载jar包操作可以看七牛云的官方文档,这里直接贴代码演示:
jar包:
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
<version>[7.2.0, 7.2.99]</version>
</dependency>
先是配置文件设置:
# 七牛云配置
qiniu.access-key=去七牛云看自己的
qiniu.secret-key=去七牛云看自己的
qiniu.bucket=存储空间的名字
# [{'zone0':'华东'}, {'zone1':'华北'},{'zone2':'华南'},{'zoneNa0':'北美'},{'zoneAs0':''}]
# 这里是存储空间的区域,建议选择华东,在七牛云自己配置,这里我们公司使用的华南
qiniu.zone=zone2
# 存储空间访问的路径,七牛云设置,推荐使用https:因为听说安卓9以上不支持http协议了
qiniu.domain-of-bucket=路径
# 链接过期时间,单位是秒,3600代表1小时,-1代表永不过期
qiniu.expire-in-seconds=-1
接着看一下七牛云的工具类代码:
import com.qiniu.common.Zone;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.util.Properties;
/**
* 七牛云配置
*
* @author simon
* @create 2018-08-15 10:44
**/
@Slf4j
@Data
public class QiNiuConfig {
private String accessKey;
private String secretKey;
private String bucket;
private Zone zone;
private String domainOfBucket;
private long expireInSeconds;
private static QiNiuConfig instance = new QiNiuConfig();
private QiNiuConfig(){
Properties prop = new Properties();
try {
prop.load(QiNiuConfig.class.getResourceAsStream("/application.properties"));
accessKey = prop.getProperty("qiniu.access-key");
secretKey = prop.getProperty("qiniu.secret-key");
bucket = prop.getProperty("qiniu.bucket");
domainOfBucket = prop.getProperty("qiniu.domain-of-bucket");
expireInSeconds = Long.parseLong(prop.getProperty("qiniu.expire-in-seconds"));
String zoneName = prop.getProperty("qiniu.zone");
if(zoneName.equals("zone0")){
zone = Zone.zone0();
}else if(zoneName.equals("zone1")){
zone = Zone.zone1();
}else if(zoneName.equals("zone2")){
zone = Zone.zone2();
}else if(zoneName.equals("zoneNa0")){
zone = Zone.zoneNa0();
}else if(zoneName.equals("zoneAs0")){
zone = Zone.zoneAs0();
}else{
throw new Exception("Zone对象配置错误!");
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static QiNiuConfig getInstance(){
return instance;
}
public static void main(String[] args) {
System.out.println(QiNiuConfig.getInstance().getAccessKey());
}
}
接着七牛云的demo代码:
import com.google.gson.Gson;
import com.qiniu.common.QiniuException;
import com.qiniu.http.Response;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.DefaultPutRet;
import com.qiniu.util.Auth;
import com.qiniu.util.UrlSafeBase64;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import org.springframework.web.multipart.MultipartFile;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
/**
* 七牛上传下载工具类
*
* @author simon
* @create 2018-08-15 11:21
**/
@SuppressWarnings("all")
@Slf4j
public class QiNiuUtil {
/**
* 上传本地文件
* @param localFilePath 本地文件完整路径
* @param key 文件云端存储的名称
* @param override 是否覆盖同名同位置文件
* @return
*/
public static boolean upload(String localFilePath, String key, boolean override){
//构造一个带指定Zone对象的配置类
Configuration cfg = new Configuration(QiNiuConfig.getInstance().getZone());
//...其他参数参考类注释
UploadManager uploadManager = new UploadManager(cfg);
//...生成上传凭证,然后准备上传
Auth auth = Auth.create(QiNiuConfig.getInstance().getAccessKey(), QiNiuConfig.getInstance().getSecretKey());
String upToken;
if(override){
upToken = auth.uploadToken(QiNiuConfig.getInstance().getBucket(), key);//覆盖上传凭证
}else{
upToken = auth.uploadToken(QiNiuConfig.getInstance().getBucket());
}
try {
Response response = uploadManager.put(localFilePath, key, upToken);
//解析上传成功的结果
DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
System.out.println(putRet.key);
System.out.println(putRet.hash);
return true;
} catch (QiniuException ex) {
Response r = ex.response;
System.err.println(r.toString());
try {
System.err.println(r.bodyString());
} catch (QiniuException ex2) {
//ignore
return false;
}
return false;
}
}
/**
* 上传Base64图片
* @param l 图片没经过base64处理的原图字节大小,获取文件大小的时候,切记要通过文件流的方式获取。而不是通过图片标签然后转换后获取。
* @param file64 图片base64字符串
* @param key 文件云端存储的名称
* @param override 是否覆盖同名同位置文件
* @return
* @throws IOException
*/
public static boolean uploadBase64(int l, String file64, String key, boolean override) throws IOException{
/*FileInputStream fis = null;
int l = (int) (new File(localFilePath).length());
byte[] src = new byte[l];
try {
fis = new FileInputStream(new File(localFilePath));
fis.read(src);
}catch (FileNotFoundException e){
e.printStackTrace();
log.error(e.getMessage());
log.error("图片文件读取失败");
return false;
}
String file64 = Base64.encodeToString(src, 0);*/
Auth auth = getAuth();
String upToken;
if(override){
upToken = auth.uploadToken(QiNiuConfig.getInstance().getBucket(), key);//覆盖上传凭证
}else{
upToken = auth.uploadToken(QiNiuConfig.getInstance().getBucket());
}
//这里需要自己更改,看区域,对应七牛云的官方文档
String url = "https://upload-z2.qiniup.com/putb64/" + l+"/key/"+ UrlSafeBase64.encodeToString(key);
//非华东空间需要根据注意事项 1 修改上传域名
RequestBody rb = RequestBody.create(null, file64);
Request request = new Request.Builder().
url(url).
addHeader("Content-Type", "application/octet-stream")
.addHeader("Authorization", "UpToken " + upToken)
.post(rb).build();
//System.out.println(request.headers());
OkHttpClient client = new OkHttpClient();
okhttp3.Response response = client.newCall(request).execute();
//System.out.println(response);
return response.isSuccessful();
}
/**
* 获取文件访问地址
* @param fileName 文件云端存储的名称
* @return
* @throws UnsupportedEncodingException
*/
public static String fileUrl(String fileName) throws UnsupportedEncodingException {
String encodedFileName = URLEncoder.encode(fileName, "utf-8");
String publicUrl = String.format("%s/%s", QiNiuConfig.getInstance().getDomainOfBucket(), encodedFileName);
Auth auth = getAuth();
long expireInSeconds = QiNiuConfig.getInstance().getExpireInSeconds();
if(-1 == expireInSeconds){
return auth.privateDownloadUrl(publicUrl);
}
return auth.privateDownloadUrl(publicUrl, expireInSeconds);
}
/**
* 上传MultipartFile
* @param file
* @param key
* @param override
* @return
* @throws IOException
*/
public static boolean uploadMultipartFile(MultipartFile file, String key, boolean override) {
//构造一个带指定Zone对象的配置类
Configuration cfg = new Configuration(QiNiuConfig.getInstance().getZone());
//...其他参数参考类注释
UploadManager uploadManager = new UploadManager(cfg);
//把文件转化为字节数组
InputStream is = null;
ByteArrayOutputStream bos = null;
try {
is = file.getInputStream();
bos = new ByteArrayOutputStream();
byte[] b = new byte[1024];
int len = -1;
while ((len = is.read(b)) != -1){
bos.write(b, 0, len);
}
byte[] uploadBytes= bos.toByteArray();
Auth auth = getAuth();
String upToken;
if(override){
upToken = auth.uploadToken(QiNiuConfig.getInstance().getBucket(), key);//覆盖上传凭证
}else{
upToken = auth.uploadToken(QiNiuConfig.getInstance().getBucket());
}
//默认上传接口回复对象
DefaultPutRet putRet;
//进行上传操作,传入文件的字节数组,文件名,上传空间,得到回复对象
Response response = uploadManager.put(uploadBytes, key, upToken);
putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
System.out.println(putRet.key);//key 文件名
System.out.println(putRet.hash);//hash 七牛返回的文件存储的地址,可以使用这个地址加七牛给你提供的前缀访问到这个视频。
return true;
}catch (QiniuException e){
e.printStackTrace();
return false;
}catch (IOException e) {
e.printStackTrace();
return false;
}
}
public static Auth getAuth(){
Auth auth = Auth.create(QiNiuConfig.getInstance().getAccessKey(), QiNiuConfig.getInstance().getSecretKey());
return auth;
}
}
七牛云截图
关于其他的七牛云的上传具体问题,这里不做演示,有问题可以留言,我会回复,中间还需要使用到一个视频截图的技术,本来我们使用MultipartFile的时候,使用的opencv,但是使用七牛云就方便了许多,只需要一行代码即可截图!!!
最后的数字代表第几秒:
http://访问路径/video1?vframe/jpg/offset/1
总结
是不是很简单!然而我使用opencv做截图需要一个500M以上的jar包加数百行代码来进行操作!!!
但是后续项目又不使用七牛云了,那没办法,还是得继续使用opencv来进行操作…
过几天会介绍使用opeccv来进行操作视频并截图和制作gif
本篇文章到此结束,如有问题,欢迎留言,我会回复,谢谢各位的观看