使用fastdfs上传视频并使用FFmpegFrameGrabberFrameGrabber在上传视频后,截图作为封面
最近为了参加一个比赛,需要一个视频上传功能,所以查了很多。以下为一个小demo
使用工具:idea,阿里云服务器,fastdfs
服务器部分
1. 在阿里云服务器上安装fastdfs分布式文件管理系统
安装方法参考https://www.cnblogs.com/handsomeye/p/9451568.html
安装fastdfs踩过太多坑了,安装一定要注意,storage.conf,client.conf,tracker.conf这三个配置文件的路径设置什么的,然后要搭配nginx实现访问,nginx的配置文件nginx.conf也要注意 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200404235834783.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDM0MDcyNg==,size_16,color_FFFFFF,t_70#pic_center)
后端部分
fastdfs和nginx配置并测试好了之后,开始写测试代码
1.idea文件结构如图
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200404235903579.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDM0MDcyNg==,size_16,color_FFFFFF,t_70)
2.在idea新建springboot项目,导入相关依赖
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-test
test
org.junit.vintage
junit-vintage-engine
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-thymeleaf
net.oschina.zcx7878
fastdfs-client-java
1.27.0.0
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.1.1
mysql
mysql-connector-java
runtime
org.springframework.boot
spring-boot-starter-data-jdbc
org.projectlombok
lombok
provided
org.bytedeco
javacv
0.8
org.apache.httpcomponents
httpcore
4.4.9
org.springframework
spring-test
5.1.6.RELEASE
3.往resources中添加一个fastdfs的配置文件fastdfs-client.properties
## fastdfs-client.properties
fastdfs.connect_timeout_in_seconds = 5
fastdfs.network_timeout_in_seconds = 30
fastdfs.charset = UTF-8
fastdfs.http_anti_steal_token = false
fastdfs.http_secret_key = FastDFS1234567890
fastdfs.http_tracker_http_port = 80
#你服务器的地址
fastdfs.tracker_servers = xx.xx.xx.xx:22122
## Whether to open the connection pool, if not, create a new connection every time
fastdfs.connection_pool.enabled = true
## max_count_per_entry: max connection count per host:port , 0 is not limit
fastdfs.connection_pool.max_count_per_entry = 500
## connections whose the idle time exceeds this time will be closed, unit: second, default value is 3600
fastdfs.connection_pool.max_idle_time = 3600
## Maximum waiting time when the maximum number of connections is reached, unit: millisecond, default value is 1000
fastdfs.connection_pool.max_wait_time_in_ms = 1000
4.application.yml
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200405000129517.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDM0MDcyNg==,size_16,color_FFFFFF,t_70)
5.编写FastDFSVideoUtils工具类
import org.csource.common.MyException;
import org.csource.fastdfs.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
public class FastDFSVideoUtils {
private static StorageClient1 client1;
private static StorageServer storeStorage;
private static StorageServer storageServer;
static{
try {
ClientGlobal.initByProperties("fastdfs-client.properties");
TrackerClient trackerClient = new TrackerClient();
TrackerServer trackerServer = trackerClient.getConnection();
storeStorage = trackerClient.getStoreStorage(trackerServer);
String storageIp = storeStorage.getSocket().getInetAddress().getHostAddress();
Integer port = storeStorage.getSocket().getPort();
//0表示上传到图片目录,1表示上传到视频目录
storageServer = new StorageServer(storageIp, port, 1);
client1 = new StorageClient1(trackerServer, storageServer);
} catch (IOException e) {
e.printStackTrace();
} catch (MyException e) {
e.printStackTrace();
}
}
public static String upload(MultipartFile file){
String oldName = file.getOriginalFilename();
try {
return client1.upload_file1(file.getBytes(), oldName.substring(oldName.lastIndexOf(".")+1),null);
} catch (IOException e) {
e.printStackTrace();
} catch (MyException e) {
e.printStackTrace();
}
return null;
}
}
还有截图工具类:
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.UUID;
import javax.imageio.ImageIO;
import org.bytedeco.javacpp.opencv_core.IplImage;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.springframework.web.multipart.MultipartFile;
public class ScreenshotUtils {
/**
* 获取指定视频的帧并保存为图片至指定目录
* @param videourl 源视频文件路径
* @throws Exception
*/
public static String fetchFrame(String videourl,MultipartFile file)
throws Exception {
//获取当前系统时间,类似new Date(),效率比较好
long start = System.currentTimeMillis();
//储存截图的文件
//window下用\\,电脑要有D盘,不然换成你想要的盘
File targetFile = new File("D:\\video\\cutpic");
if(!targetFile.exists()){
targetFile.mkdirs();
}
String filename = file.getOriginalFilename();
String filenamePrefix = filename.substring(0, filename.lastIndexOf("."));
//创建储存截图的图片文件路径
//window下用\\
String coverimgPath = targetFile.getPath()+ "\\" + UUID.randomUUID().toString()+filenamePrefix + ".jpg";
File cutpic = new File(coverimgPath);
//FFmpegFrameGrabb读取时间随机截图类
FFmpegFrameGrabber ff = new FFmpegFrameGrabber(videourl);
ff.start();
// 表示视频的总图片数量
int lenght = ff.getLengthInFrames();
int i = 0;
Frame f = null;
while (i
break;
}
i++;
}
IplImage img = f.image;
int owidth = img.width();
int oheight = img.height();
// 对截取的帧进行等比例缩放
int width = 800;
int height = (int) (((double) width / owidth) * oheight);
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
bi.getGraphics().drawImage(f.image.getBufferedImage().getScaledInstance(width, height, Image.SCALE_SMOOTH),
0, 0, null);
ImageIO.write(bi, "jpg", cutpic);
//ff.flush();
ff.stop();
System.out.println(System.currentTimeMillis() - start);
return coverimgPath;
}
}
6.编写mapper文件和mapper.xml
这里因为我的mybatis-config.xml有问题,不知道什么错,所以先用注解方式测试,就可以不用xml文件了
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200405000205179.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDM0MDcyNg==,size_16,color_FFFFFF,t_70)
INSERT INTO video(video_url,user_id,professional_name,image_url,title,create_time,collection_count,thumb_count,visit_count,comment_count)
VALUES (#{videoUrl},#{userId},#{professionalName},#{imageUrl},#{title},#{createTime},#{collectionCount},#{thumbCount},#{visitCount},#{commentCount})
mybatis-config.xml 数据库我建在了服务器上,要先安装mysql服务在服务器上哦,结构如下,连接数据库的方法请自行百度
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200405000249317.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDM0MDcyNg==,size_16,color_FFFFFF,t_70)
7.VideoSaveService和VideoSaveServiceImpl
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200405000312809.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDM0MDcyNg==,size_16,color_FFFFFF,t_70)
import com.upload.demo.config.FastDFSUtils;
import com.upload.demo.config.FastDFSVideoUtils;
import com.upload.demo.config.ScreenshotUtils;
import com.upload.demo.mapper.VideoSaveMapper;
import com.upload.demo.pojo.VideoSave;
import com.upload.demo.service.VideoSaveService;
import org.apache.http.entity.ContentType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.FileInputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
@Service
public class VideoSaveServiceImpl implements VideoSaveService {
private VideoSaveMapper videoSaveMapper;
@Autowired
VideoSaveServiceImpl(VideoSaveMapper videoSaveMapper){
this.videoSaveMapper = videoSaveMapper;
}
@Value("${fastdfs.nginx.host}")
String nginxHost;
@Override
public Integer insertVideo(MultipartFile file,
Integer userId,
String professionalName,
String title,
Integer collectionCount,
Integer thumbCount,
Integer visitCount,
Integer commentCount) throws Exception {
final String fileId = FastDFSVideoUtils.upload(file);
String videoUrl = nginxHost + fileId;
Date date = new Date();
//HH为24小时制,hh为12小时制
SimpleDateFormat dateFormat= new SimpleDateFormat("yyyy-MM-dd :HH:mm:ss");
String createTime = dateFormat.format(date);
//将截图转化为file对象,再将file对象转化为MockMultipartFile 对象
String cutpicPath = ScreenshotUtils.fetchFrame(videoUrl, file);
File cutpic = new File(cutpicPath);
FileInputStream fileInputStream = new FileInputStream(cutpic);
MockMultipartFile cutPicFile = new MockMultipartFile(cutpic.getName(),
cutpic.getName(), ContentType.APPLICATION_OCTET_STREAM.toString(), fileInputStream);
//FastDFSUtils将上面转化的MockMultipartFile 对象上传
final String cutPicfileId = FastDFSVideoUtils.upload(cutPicFile);
String imageUrl = nginxHost + cutPicfileId;
return videoSaveMapper.insertVideoUrl(videoUrl,userId,professionalName,imageUrl,title,createTime,collectionCount,thumbCount,visitCount,commentCount);
}
}
8.VideoUploadController控制器代码
import com.upload.demo.config.FastDFSUtils;
import com.upload.demo.config.FastDFSVideoUtils;
import com.upload.demo.config.ScreenshotUtils;
import com.upload.demo.service.VideoSaveService;
import org.apache.http.entity.ContentType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.FileInputStream;
@Controller
public class VideoUploadController {
@Value("${fastdfs.nginx.host}")
String nginxHost;
private VideoSaveService videoSaveService;
@Autowired
VideoUploadController(VideoSaveService videoSaveService){
this.videoSaveService = videoSaveService;
}
@PostMapping("/uploadVideo")
public String UploadVideo(MultipartFile file,
@RequestParam(value="userId",defaultValue="1") Integer userId,
@RequestParam(value="professionalName",defaultValue="hhh") String professionalName,
@RequestParam(value="title",defaultValue="123") String title,
@RequestParam(value="collectionCount",defaultValue="0") Integer collectionCount,
@RequestParam(value="thumbCount",defaultValue="0") Integer thumbCount,
@RequestParam(value="visitCount",defaultValue="0") Integer visitCount,
@RequestParam(value="commentCount",defaultValue="0") Integer commentCount
) throws Exception {
if(videoSaveService.insertVideo(file,userId,professionalName,title,collectionCount,thumbCount,visitCount,commentCount)==1){
return "/success";
}else {
return "/false";
}
}
}
前端部分
都要放在template文件夹下,控制器要有一个跳转到index.html下的方法,return就行,很简单。
index.html
单文件上传
成功跳转到succes.html,失败跳转false.html
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200405000339231.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDM0MDcyNg==,size_16,color_FFFFFF,t_70)
运行截图:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200405000356271.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDM0MDcyNg==,size_16,color_FFFFFF,t_70)
![在这里插入图片描述](https://img-blog.csdnimg.cn/2020040500041473.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDM0MDcyNg==,size_16,color_FFFFFF,t_70)
将url在浏览器打开
视频
视频截图 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200405000517472.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDM0MDcyNg==,size_16,color_FFFFFF,t_70)
我爱的iu
|