Java文件上传工作实战(视频,图片,七牛云,ffmpeg,文件复制等操作)

在工作中或多或少会遇见关于文件上传的功能,最近在做的项目中就涉及到了大量的文件上传,包括图片和视频等,话不多说,直接开整!!!

文章目录

      • 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
Java文件上传工作实战(视频,图片,七牛云,ffmpeg,文件复制等操作)_第1张图片
推荐安装最新版本,下载完成后去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
本篇文章到此结束,如有问题,欢迎留言,我会回复,谢谢各位的观看

你可能感兴趣的:(Java实战技术)