本文介绍两种网络技术实现方法。一是 MD5 校验秒传,服务器端用数据库记上传文件 MD5 值及存储路径,Java 代码接收客户端 MD5 值并查询校验,返回状态码。二是用 ffmpeg 切片视频成 m3u8 上传,异步合并文件实现视频按需加载。
1. 通过MD5校验实现秒传
服务器端MD5校验
- 数据库存储:
服务器端需数据库(如 MySQL、MongoDB 等)记录上传文件 MD5 值及存储路径。以 MySQL 为例,创建简单表存储相关信息。表结构中,md5_value
存文件 MD5 值,file_path
存服务器端存储路径,upload_time
记录文件上传时间。
CREATE TABLE uploaded_files (id INT AUTO_INCREMENT PRIMARY KEY,md5_value VARCHAR(32) NOT NULL UNIQUE,file_path VARCHAR(255) NOT NULL,upload_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
- 校验逻辑实现:
Java服务器端代码示例:接收客户端 MD5 值并校验(用 Spring Boot 框架简化示例,实际应用可依具体框架和需求调整)。代码中接收客户端传来的 MD5 值在数据库查询校验。查到记录则文件已存在,返回 200 状态码表示秒传成功;未查到返回 404 表示需正常上传;查询出错返回 500 表示服务器内部错误。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;@SpringBootApplication
@RestController
public class FileServerApp {private static final String JDBC_URL = "jdbc:mysql://localhost:3306/your_database_name?useSSL=false&serverTimezone=UTC";private static final String JDBC_USERNAME = "your_username";private static final String JDBC_PASSWORD = "your_password";public static void main(String[] args) {SpringApplication.run(FileServerApp.class, args);}@GetMapping("/checkFile")public ResponseEntity<String> checkFile(@RequestParam("md5") String md5) {try (Connection connection = DriverManager.getConnection(JDBC_URL, JDBC_USERNAME, JDBC_PASSWORD);PreparedStatement preparedStatement = connection.prepareStatement("SELECT file_path FROM uploaded_files WHERE md5_value =?")) {preparedStatement.setString(1, md5);ResultSet resultSet = preparedStatement.executeQuery();if (resultSet.next()) {// 文件已存在,返回200状态码return new ResponseEntity<>("文件已存在", HttpStatus.OK);} else {// 文件不存在,返回404状态码return new ResponseEntity<>("文件不存在", HttpStatus.NOT_FOUND);}} catch (SQLException e) {e.printStackTrace();// 数据库查询出错,返回500状态码return new ResponseEntity<>("服务器内部错误", HttpStatus.INTERNAL_SERVER_ERROR);}}
}
2. 用 ffmpeg 将视频切片成 m3u8 上传,采用异步合并文件实现视频按需加载
视频切割
-
参数调整:
前面切割视频示例仅设常见参数,如视频编码格式(-c:v libx264
)、音频编码格式(-c:a aac
)、切片时长(-hls_time 10
)和列表大小(-hls_list_size 0
)。实际应用中或需依具体需求调整这些参数,比如:- 视频分辨率:要支持多种分辨率视频播放,可添加不同参数设置生成不同分辨率视频切片集合,在 m3u8 文件中组织,实现自适应分辨率播放。如添加
-s:v 1280x720
和-s:v 640x360
等参数分别生成高清和标清视频切片。 - 码率设置:根据网络状况和目标设备性能,需设置不同视频码率,可通过参数如
-b:v 2000k
实现不同码率视频切片生成。
- 视频分辨率:要支持多种分辨率视频播放,可添加不同参数设置生成不同分辨率视频切片集合,在 m3u8 文件中组织,实现自适应分辨率播放。如添加
-
错误处理细化:
仅依process.waitFor()
返回值判断切割不足。实际应用中应获取ffmpeg
执行输出信息定位问题。以下是修改后示例,读取ffmpeg
标准输出和错误输出信息处理切割问题。
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;public class VideoSliceUtil {public static void sliceVideo(File videoFile, String outputDir) {try {String ffmpegPath = "ffmpeg"; // 根据实际安装路径修改String command = ffmpegPath + " -i " + videoFile.getAbsolutePath() + " -c:v libx264 -c:a aac -f hls -hls_time 10 -hls_list_size 0 " + outputDir + "/video.m3u8";Process process = Runtime.getRuntime().exec(command);// 读取标准输出和标准错误输出信息InputStream stdout = process.getInputStream();InputStream stderr = process.getErrorStream();BufferedReader stdoutReader = new BufferedReader(new InputStreamReader(stdout));BufferedReader stderrReader = new BufferedReader(new InputStreamReader(stderr));List<String> stdoutMessages = new ArrayList<>();List<String> stderrMessages = new ArrayList<>();String line;while ((line = stdoutReader.readLine())!= null) {stdoutMessages.add(line);}while ((line = stderrReader.readLine())!= null) {stderrMessages.add(line);}int exitCode = process.waitFor();if (exitCode == 0) {System.out.println("视频切割成功");if (!stdoutMessages.isEmpty()) {System.out.println("标准输出信息:");for (String msg : stdoutMessages) {System.out.println(msg);}}} else {System.out.println("视频切割失败");if (!stderrMessages.isEmpty()) {System.out.println("标准错误输出信息:");for (String msg : stderrMessages) {System.out.println(msg);}}}// 关闭流stdoutReader.close();stderrReader.close();stdout.close();stderr.close();} catch (Exception e) {e.printStackTrace();}}
}
异步合并文件
- 具体合并逻辑实现:
使用FFmpegFrameRecorder
库合并视频片段。假设切割所得片段在指定目录且 m3u8 文件已正确生成。先创建FFmpegFrameRecorder
对象,设置视频、音频编码格式及输出文件格式。遍历视频片段,用FrameGrabber
读取帧,通过recorder
记录到合并文件中。
import java.io.File;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.ffmpeg.global.swscale;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.FrameGrabber;
import org.bytedeco.javacv.OpenCVFrameGrabber;public class FileMergeAsync {public static void mergeFilesAsync(final File[] filesToMerge, final String outputFile) {ExecutorService executor = Executors.newSingleThreadExecutor();executor.submit(new Runnable() {@Overridepublic void run() {try {// 创建FFmpegFrameRecorder对象用于合并视频FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputFile, -1);recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);recorder.setAudioCodec(avutil.AV_CODEC_ID_AAC);recorder.setFormat("mp4");recorder.start();// 遍历每个视频片段进行合并for (File file : filesToMerge) {FrameGrabber grabber = new OpenCVFrameGrabber(file);grabber.start();Frame frame;while ((frame = grabber.read())!= null) {recorder.record(frame);}grabber.stop();}recorder.stop();System.out.println("文件合并成功");} catch (Exception e) {e.printStackTrace();System.out.println("文件合并失败");} finally {executor.shutdown();}}});}
}
-
资源管理与优化:
合并文件时涉及多个对象创建与使用,如FFmpegFrameRecorder
、FrameGrabber
等,需注意资源管理与优化。用后及时调用stop()
释放资源,避免内存泄漏。对可能的异常,在异常处理代码中正确关闭和释放资源。 -
异步处理的监控与反馈:
前面异步合并文件示例仅提交任务后打印成功或失败消息。实际应用中需对异步任务深入监控并提供更多反馈。可通过添加回调函数或用异步任务监控框架了解进度、异常等情况并反馈给相关方。