java大视频分片上传

devtools/2024/11/27 13:48:46/

实现原理,前端控制每次上传1mb,后端接受1mb,并记录该分片下标,返回给前端还未上传的下标,直到所有的都上传完成

controller

java">@ApiOperation(value = "上传视频", notes = "上传视频", httpMethod = "POST", response = WebResult.class)
@PostMapping(value = "/uploadVideo", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)//
public AjaxResult uploadVideo(@RequestParam(name = "file") MultipartFile file,@RequestParam(name = "chunkIndex") Integer chunkIndex,@RequestParam(name = "md5") String md5,@RequestParam(name = "totalFileSize") Long totalFileSize,@RequestParam(name = "fileName") String fileName,@RequestParam(name = "userName") String userName,HttpServletRequest request) throws Exception {if (null == file || file.isEmpty()) {return AjaxResult.error("文件内容不能为空!");}return fileSystemService.uploadVideo(file, chunkIndex, md5, fileName, userName);}

service

java">public AjaxResult uploadVideo(MultipartFile file, Integer chunkIndex, String md5, String fileName, String userName) throws IOException {//创建存放文件夹String date = DateTime.now().toString("yyyyMMdd");String dirPath = ConstantUtils.FILE_VIDEO_PATH + userName + "/" + date + "/" + md5 + "/";File dirFile = new File(dirPath);if (!dirFile.exists()){dirFile.mkdirs();}//分区文件String relativeFilePath = dirPath + fileName;File tempFile = new File(relativeFilePath);RandomAccessFile rw = new RandomAccessFile(tempFile, "rw");//定位到分片的偏移量rw.seek(Long.parseLong(ConstantUtils.FILE_VIDEO_CHUNK_SIZE) * chunkIndex);//写入分片数据rw.write(file.getBytes());//关闭流rw.close();//读取已经分片的集合Set<Object> hasChunkList = new HashSet<>();String hasChunkKey = ConstantUtils.CHUNK_PREFIX + md5;if (redisHelper.hasKey(hasChunkKey)){Object o = redisHelper.get(hasChunkKey);JSONArray array = JSONUtil.parseArray(o);hasChunkList.addAll(array);}hasChunkList.add(chunkIndex);//最新分片下标跟新到redisredisHelper.set(hasChunkKey,hasChunkList);HashMap<String, Object> map = new HashMap<>();map.put("url",userName + "/" + date + "/" + md5 + "/" + fileName);map.put("hasList",hasChunkList);return AjaxResult.success(map);}

ConstantUtils

java">package com.ruoyi.file.utils;import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;/*** @author csb* @description: 获取配置文件常量数据* @date 2023/9/20*/// spring初始化bean的时候,如果bean实现了InitializingBean接口,
// 会自动调用afterPropertiesSet方法
@Component
public class ConstantUtils implements InitializingBean {@Value("${file.video.videoPath}")String videoPath;@Value("${file.video.chunkSize}")String videoChunkSize;public static String FILE_VIDEO_PATH;public static String FILE_VIDEO_CHUNK_SIZE;public static String CHUNK_PREFIX = "FILE_VIDEO";@Overridepublic void afterPropertiesSet() throws Exception {FILE_VIDEO_PATH = videoPath;FILE_VIDEO_CHUNK_SIZE = videoChunkSize;}
}

RedisHelper

java">package com.ruoyi.file.utils;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;@Component
public class RedisHelper {private static final Logger logger = LoggerFactory.getLogger(RedisHelper.class);public static final int CACHE_TIME_1_YEAR = 60 * 60 * 24 * 365;@Resourceprivate RedisTemplate<Object, Object> redisTemplate;/*** 读取缓存** @param key* @return*/public Object get(final String key) {return redisTemplate.opsForValue().get(key);}/*** 写入缓存*/public boolean set(final String key, Object value) {boolean result = false;try {redisTemplate.opsForValue().set(key, value);result = true;} catch (Exception e) {logger.warn("", e);}return result;}/*** 写入缓存*/public boolean set(final String key, Object value, int seconds) {boolean result = false;try {if (seconds > 0) {redisTemplate.opsForValue().set(key, value, seconds, TimeUnit.SECONDS);} else {redisTemplate.opsForValue().set(key, value);}result = true;} catch (Exception e) {logger.warn("", e);}return result;}public boolean hasKey(String key) {return redisTemplate.hasKey(key);}public boolean exist(final String key) {return redisTemplate.hasKey(key);}/*** 更新缓存*/public boolean getAndSet(final String key, Object value) {boolean result = false;try {redisTemplate.opsForValue().getAndSet(key, value);result = true;} catch (Exception e) {logger.warn("", e);}return result;}/*** 删除缓存*/public boolean delete(final String key) {boolean result = false;try {redisTemplate.delete(key);result = true;} catch (Exception e) {logger.warn("", e);}return result;}/*** 1.Redis设置多个值* 命令:HMSET myhash total 15 success 0 time "2019-12-01 11:10:15"* 2.当一个任务完成之后,把成功的次数加1* 命令:HINCRBY myhash success 1* <p>* 当前有多少个计算(有点过几次计算)* 1.列表中添加值* 命令:RPUSH mylist "hello"* 2.获取列表中所有元素* 命令:LRANGE mylist 0 -1*//*** 设置hash值,同时设置多个属性** @param key 键* @param map 多个属性值用map封装* @return*/public boolean hmset(final String key, Map<String, Object> map) {boolean result = false;try {HashOperations<Object, Object, Object> opsForHash = redisTemplate.opsForHash();opsForHash.putAll(key, map);result = true;} catch (Exception e) {logger.warn("", e);}return result;}/*** 自增值,给hash值某个属性自增** @param key   键* @param field 要自增的属性* @param num   自增值的大小,可以为正数负数* @return*/public boolean hincrby(final String key, String field, Integer num) {boolean result = false;try {HashOperations<Object, Object, Object> opsForHash = redisTemplate.opsForHash();opsForHash.increment(key, field, num);result = true;} catch (Exception e) {logger.warn("", e);}return result;}/*** 自增值,给hash值某个属性自增1** @param key   键* @param field 要自增的属性* @return*/public boolean hincrby(final String key, String field) {boolean result = false;try {HashOperations<Object, Object, Object> opsForHash = redisTemplate.opsForHash();opsForHash.increment(key, field, 1);result = true;} catch (Exception e) {logger.warn("", e);}return result;}/*** 获取hash中的所有数据** @param key* @return*/public Map<Object, Object> hgetall(final String key) {Map<Object, Object> entries = new HashMap<>();try {HashOperations<Object, Object, Object> opsForHash = redisTemplate.opsForHash();entries = opsForHash.entries(key);return entries;} catch (Exception e) {logger.warn("", e);}return entries;}/*** list操作,队列右侧添加值** @param key* @param value* @return*/public boolean rpush(final String key, Object value) {boolean result = false;try {ListOperations<Object, Object> opsForList = redisTemplate.opsForList();opsForList.rightPush(key, value);result = true;} catch (Exception e) {logger.warn("", e);}return result;}/*** 获取列表中的所有元素** @param key* @return*/public List<Object> lrange(final String key) {List<Object> range = new ArrayList<>();try {ListOperations<Object, Object> opsForList = redisTemplate.opsForList();range = opsForList.range(key, 0, -1);return range;} catch (Exception e) {logger.warn("", e);}return range;}/*** 删除list中的值** @param key   list的key* @param value 要删除的list中的value* @return*/public boolean lrem(final String key, Object value) {boolean result = false;try {ListOperations<Object, Object> opsForList = redisTemplate.opsForList();opsForList.remove(key, 0, value);result = true;} catch (Exception e) {logger.warn("", e);}return result;}/*** 设置键的过期时间* @param key 键* @param expiredTimeSecond 过期时间(秒)* @return*/public boolean setKeyExpiredTime(String key, Long expiredTimeSecond){return this.redisTemplate.expire(key, expiredTimeSecond,TimeUnit.SECONDS);}}

http://www.ppmy.cn/devtools/137404.html

相关文章

Win7电脑IP地址查看与变换指南

在Windows 7操作系统中&#xff0c;IP地址作为网络通讯的基础信息&#xff0c;对于日常的网络管理和故障排查至关重要。了解如何查看和变换IP地址&#xff0c;可以帮助用户更好地掌握网络状态&#xff0c;优化网络配置。本文将详细介绍在Win7电脑ip地址在哪查看以及Win7电脑ip地…

[含文档+PPT+源码等]精品大数据项目-Django基于大数据实现的游戏用户行为分析与个性化推荐系统

一、项目背景 大数据技术的发展&#xff1a; 随着大数据技术的不断发展和普及&#xff0c;越来越多的行业开始利用大数据进行业务分析和决策。大数据具有数据量大、数据类型多样、处理速度快等特点&#xff0c;为数据分析和个性化推荐提供了强大的技术支持。 游戏产业的繁荣&am…

Windows Pycharm 远程 Spark 开发 PySpark

一、环境版本 环境版本PyCharm2024.1.2 (Professional Edition)Ubuntu Kylin16.04Hadoop3.3.5Hive3.1.3Spark2.4.0 二、Pycharm远程开发 文件-远程-开发 选择 SSH连接&#xff0c;连接虚拟机&#xff0c;选择项目目录即可远程开发

UE5 fieldSystemActor类

在UE5&#xff08;虚幻引擎5&#xff09;中&#xff0c;FieldSystemActor 是用于处理和模拟场景中的物理力场&#xff08;Field Systems&#xff09;的一个重要类。该类提供了一种机制&#xff0c;用于生成和控制力场&#xff0c;进而影响场景中的物理对象。FieldSystemActor 的…

redmi 12c 刷机

刷机历程 一个多月前网购了redmi 12c这款手机, 价格只有550,用来搞机再适合不过了, 拆快递后就开始倒腾,网上有人说需要等7天才能解锁,我绑定了账号过了几天又忍不住倒腾,最后发现这块手机不用等7天解锁成功了,开始我为了获取root权限, 刷入了很火的magisk,但是某一天仍然发现/…

Flink解决延迟数据问题

总结&#xff1a; 水印&#xff1a;对于迟到数据不长 allowedLateness: 迟到时间很长 侧道输出&#xff1a;对于迟到时间特别长 对于延迟数据的理解&#xff1a; 水印机制(水位线、watermark)机制可以帮助我们在短期延迟下&#xff0c;允许乱序数据的到来。 这个机制很好的…

SpringBoot(四十)SpringBoot集成RabbitMQ使用过期时间+死信队列实现延迟队列

前边我们使用RabbitMQ实现了高并发下对流量的削峰填谷。正常在实际应用中大概也就够用了。 有的时候呢&#xff0c;我们需要使用到延迟队列&#xff0c;RabbitMQ不像RocketMQ一样默认就支持延迟队列&#xff0c;RabbitMQ是不支持延迟队列的&#xff0c;但是呢&#xff1f;我们可…

Android8设置拔出充电器自动关机

通常Android机器拔出充电后&#xff0c;将进入断开充电流程&#xff0c;关闭充电灯和充电图标。 那么需要实现拔出充电器直接进入关机&#xff0c;则需要在充电判断机制中额外增加实现代码。 || || 修改方案如下&#xff1a; 在系统中存在服务时刻监听的充电状态&#xff…