背景
项目中很多预览工具,文件转pdf预览,采用libreoffice6.1插件实现
环境说明
系统CentOS:CentOS7
libreoffice:6.1
下载
中文官网 https://zh-cn.libreoffice.org/download/libreoffice/
下载其他老版本
某云盘
安装
1.上传到/opt 目录
2.解压
tar -zxvf LibreOffice_6.1.6_Linux_x86-64_rpm.tar.gz
3.安装
cd LibreOffice_6.1.6.3_Linux_x86-64_rpm/RPMS
yum localinstall *.rpm
4.查看安装目录
which libreoffice6.1
转化测试
在/opt 目录上传测试文件
执行
/usr/bin/libreoffice6.1 --headless --invisible --convert-to pdf ./需求梳理.docx --outdir ./
生成需求梳理.pdf 并下载到本地
打开中文乱码
解决中文乱码
看原有字体
cd /usr/share/fonts/
安装字体
yum groupinstall "fonts"
查看 /usr/share/fonts/目录下出现很多字体
重新生成pdf测试
下载 打开后不乱码 这是我打马赛克
安装出现的问题
转化是出现 错误
/opt/libreoffice6.1/program/soffice.bin: error while loading shared libraries: libcairo.so.2: cannot open shared object file: No such file or directory
到/opt/libreoffice6.1/program/ 目录下执行
soffice -help
报错
/opt/libreoffice6.1/program/soffice.bin: error while loading shared libraries: libcairo.so.2: cannot open shared object file: No such file or directory
执行命令:
yum install cairo
后再次执行命令:/opt/libreoffice6.1/program/soffice -help
报错
/opt/libreoffice6.1/program/soffice.bin: error while loading shared libraries: libcairo.so.2: cannot open shared object file: No such file or directory
yum install cups-libs
后,再次执行命令:/opt/libreoffice6.1/program/soffice -help
报错
/opt/libreoffice6.1/program/soffice.bin: error while loading shared libraries: libcairo.so.2: cannot open shared object file: No such file or directory
yum install libSM
后,再次执行:/opt/libreoffice6.1/program/soffice -help
libreoffice的相关命令出来了,libreoffice安装成功
通过java结合libreoffice 转pdf
实现文件预览
代码
package com.cloud.user.controller;
import com.cloud.user.bean.po.CloudFile;
import com.cloud.user.service.UploaderService;
import com.cloud.user.util.FileUploadUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.util.List;
@RestController
@RequestMapping("uploader")
public class TestUploader {
//-----------------------普通上传----------------非分片
/**
*普通上传 和 上传时并生成pdf文件 用于预览 也可抽出来单独 调用请求预览 此处生成预览前置 避免每次预览时生成
* @param file
* @return
*/
@PostMapping("/uploadFiles")
public String uploadFiles(@RequestParam("file") MultipartFile[] file) {
List<CloudFile > list= uploaderService.uploadFiles(file);
return "SUCCESS";
}
}
package com.cloud.user.service;
import com.cloud.user.bean.po.CloudFile;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
public interface UploaderService {
/**
* 检查分片是否存在
* @param md5Value
* @param chunk
* @param moduleName
* @param fileTypeKey
* @return
*/
Boolean checkChunk(String md5Value, Integer chunk, String moduleName, String fileTypeKey);
/**
* 上传分片
* @param file
* @param md5Value
* @param chunk
* @param moduleName
* @param fileTypeKey
* @return
*/
Boolean uploadChunk(MultipartFile file, String md5Value, Integer chunk, String moduleName, String fileTypeKey);
/**
* 合并分片
* @param chunkCount
* @param md5Value
* @param fileName
* @param moduleName
* @param fileTypeKey
* @return
*/
String merge(Integer chunkCount, String md5Value, String fileName, String moduleName, String fileTypeKey);
/**
* 获取上传目录
* @param fileTypeKey
* @return
*/
String specifiedDirectory(String moduleName,String fileTypeKey);
/**
* 根据文件id 获取文件全路径
* @param fileId
* @return
*/
String getFilePathByFileId(String fileId);
/**
* 获取根路径
* @return
*/
String rootPath();
/**
* 普通上传 和 上传时并生成pdf文件 用于预览 也可抽出来单独 调用请求预览 此处生成预览前置 避免每次预览时生成
* @param file
* @return
*/
List<CloudFile> uploadFiles(MultipartFile[] file);
}
package com.cloud.user.service.impl;
import com.cloud.framework.file.ResumeUploader;
import com.cloud.framework.util.ConstantUtils;
import com.cloud.framework.util.UUIDGenerator;
import com.cloud.user.bean.po.CloudFile;
import com.cloud.user.entity.db1.PanFile;
import com.cloud.user.properties.FileProperties;
import com.cloud.user.service.UploaderService;
import com.cloud.user.util.ConvertToPdfRunable;
import com.cloud.user.util.FileUploadUtils;
import com.cloud.user.util.Md5Util;
import com.cloud.user.util.ThreadPoolUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
@Service
public class UploaderServiceImpl implements UploaderService {
private final ResumeUploader fileOperation;
private final FileProperties fileProperties;
public UploaderServiceImpl(Map<String, ResumeUploader> resumeUploader, FileProperties fileProperties) {
this.fileOperation = resumeUploader.get(ConstantUtils.fs.UNC.name());
this.fileProperties = fileProperties;
}
@Override
public Boolean checkChunk(String md5Value, Integer chunk, String moduleName, String fileTypeKey) {
String path = specifiedDirectory(moduleName, fileTypeKey);
System.out.println("路径" + path);
return fileOperation.checkChunk(path, md5Value, chunk);
}
@Override
public Boolean uploadChunk(MultipartFile file, String md5Value, Integer chunk, String moduleName, String fileTypeKey) {
String path = specifiedDirectory(moduleName, fileTypeKey);
System.out.println("路径" + path);
return fileOperation.upload(file, path, md5Value, chunk);
}
@Override
public String merge(Integer chunkCount, String md5Value, String fileName, String moduleName, String fileTypeKey) {
String path = specifiedDirectory(moduleName, fileTypeKey);
System.out.println("路径" + path);
// 目录下如果存在相同名称的文件,对文件名称做修改 xxx(1).txt xxx(2).txt
fileName = this.verifyRepeatFile(path, fileName, 0);
Boolean merge = fileOperation.merge(path, path, md5Value, chunkCount, fileName);
if (merge) {
String filePath = path + fileName;
CloudFile cloudFile = new CloudFile();
cloudFile.setId(UUIDGenerator.getUUID());
cloudFile.setMd5(Md5Util.fastMD5(new File(filePath)));
cloudFile.setName(fileName.substring(0, fileName.lastIndexOf(".")));
cloudFile.setType(fileName.substring(fileName.lastIndexOf(".")));
cloudFile.setPath(filePath);
cloudFile.setFileSize(new File(filePath).length());
cloudFile.setGmtCreate(new Date());
//存数据库
// int save = cloudFileMapper.save(cloudFile);
int save = 1;
if (save > 0) {
return cloudFile.getId();
}
}
return null;
}
/**
*
* @param fileTypeKey 文件类型
*/
/**
* 根据模块名称 文件类型 获取指定文件目录 /根目录/模块名/类型名 存储目录
*
* @param moduleName 模块名称
* @param fileTypeKey 文件类型
* @return
*/
@Override
public String specifiedDirectory(String moduleName, String fileTypeKey) {
// 文件根路径
String rootPath = fileProperties.getBasePath();
//软件
if (moduleName.equals("app")) {
rootPath = rootPath.endsWith(File.separator) ? rootPath : rootPath + File.separator;
rootPath = rootPath + "app";
}
//项目
if (moduleName.equals("project")) {
rootPath = rootPath.endsWith(File.separator) ? rootPath : rootPath + File.separator;
rootPath = rootPath + "project";
}
// 合并拼接路径
rootPath = rootPath.endsWith(File.separator) ? rootPath : rootPath + File.separator;
if (StringUtils.isNotBlank(fileTypeKey)) {
rootPath = rootPath + fileTypeKey + File.separator;
}
return rootPath;
}
@Override
public String getFilePathByFileId(String id) {
return getFileFullPath(id);
}
@Override
public String rootPath() {
// 文件根路径
String rootPath = fileProperties.getBasePath();
rootPath = rootPath.endsWith(File.separator) ? rootPath : rootPath + File.separator;
return rootPath;
}
private String getFileFullPath(String id) {
//从db查 写死模拟
//PanFile panFile = panFileManageMapper.getPanFile(id);
PanFile panFile =new PanFile();
panFile.setId(id);
panFile.setParentId("0");
panFile.setFileName("sc");
String parentId = panFile.getParentId();
if (!"0".equals(parentId)) {
return getFileFullPath(parentId) + File.separator + panFile.getFileName();
}
return File.separator + panFile.getFileName();
}
/**
* 目录下如果存在相同名称的文件,对文件名称做修改 xxx(1).txt xxx(2).txt
*
* @param path
* @param fileName
* @param i
* @return
*/
private String verifyRepeatFile(String path, String fileName, int i) {
String name = fileName.substring(0, fileName.lastIndexOf("."));
String suffix = fileName.substring(fileName.lastIndexOf("."));
String tempFileName;
if (i != 0) {
tempFileName = String.format("%s%s%d%s", name, "(", i, ")");
} else {
tempFileName = name;
}
File file = new File(path + tempFileName + suffix);
if (file.exists()) {
return verifyRepeatFile(path, name + suffix, ++i);
}
return tempFileName + suffix;
}
@Override
public List<CloudFile> uploadFiles(MultipartFile[] files) {
String targetDirectoryPath = fileProperties.getProjectPath();
File directoryFile = new File(targetDirectoryPath);
if (!directoryFile.exists()) {
directoryFile.mkdirs();
}
List<CloudFile> uploadFileList = new ArrayList<CloudFile>();
for (int i = 0; files.length > i; i++) {
long size;
MultipartFile file = files[i];
size = file.getSize();
String sizeString = FileUploadUtils.readableFileSize(size);
//上传
String path = FileUploadUtils.upload(file, targetDirectoryPath);
String prefix = FileUploadUtils.getPrefix(path);
String suffix = FileUploadUtils.getSuffix(path);
String suffixLowerCase = StringUtils.lowerCase(suffix);
CloudFile cloudFile = new CloudFile();
cloudFile.setId(UUIDGenerator.getUUID());
cloudFile.setMd5(Md5Util.fastMD5(new File(path)));
cloudFile.setName(file.getOriginalFilename());
cloudFile.setType(suffix);
cloudFile.setPath(path);
cloudFile.setFileSize(new File(path).length());
cloudFile.setGmtCreate(new Date());
uploadFileList.add(cloudFile);
//转pdf
if (!FileUploadUtils.isType(file.getName(), FileUploadUtils.PDF)) {
ConvertToPdfRunable convertToPdfRunable = new ConvertToPdfRunable(cloudFile.getId(), fileProperties.getPdfPath(), cloudFile.getPath());
ThreadPoolUtil.PDF_SINGEL_STHREAD_POOL.execute(convertToPdfRunable);
}
}
//存数据库
// int save = cloudFileMapper.saves(uploadFileList);
return uploadFileList;
}
}
配置类
package com.cloud.user.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "file.uploader")
public class FileProperties {
private String basePath;
private String projectPath;
private String pdfPath;
}
#type文件上传类型 account连接地址 pwd:密码 temp分片临时目录 isDelTempFile临时目录是否删除
uploader:
type: unc
account:
pwd:
tempdir: temp
isDelTempFile: true
# 文件上传路径 linux basePath:/opt/dcp/ projectPath:${file.upload.basePath}project/ wind: basePath: D:\upload\ projectPath: ${file.upload.basePath}project\
file:
uploader:
basePath: /opt/dcp/
projectPath: ${file.uploader.basePath}project/
pdfPath: ${file.uploader.basePath}pdfTemp/
工具类
package com.cloud.user.util;
import cn.hutool.core.io.FileUtil;
import lombok.extern.slf4j.Slf4j;
import java.io.File;
/**
* 文件转pdf 采用libreoffice 插件
*/
@Slf4j
public class ConvertToPdfRunable implements Runnable {
/**
* 生成pdf文件地址
*/
private String pdfPath;
/**
* 文件id
*/
private String id;
/**
* 文件真实路径
*/
private String filePath;
public ConvertToPdfRunable(String id, String pdfPath, String filePath){
this.pdfPath = pdfPath;
this.id = id;
this.filePath=filePath;
}
@Override
public void run() {
log.info("转pdf文档");
boolean flag = false;
String fileName = FileUploadUtils.getName(filePath);
String suffix = FileUploadUtils.getSuffix(filePath);
if (!CommonConstantUtils.FILE_TYPE_FILTER.contains(suffix)){
log.info("{}类型不可转pdf格式",suffix);
return;
}
log.info("文件名:{}",fileName);
//处理文件名特殊字符 否则会 转pdf 失败
for (char c : CommonConstantUtils.PDF_FILTER) {
if (fileName.contains(String.valueOf(c))) {
flag = true;
break;
}
}
if (flag){
for (char c : CommonConstantUtils.PDF_FILTER) {
fileName =fileName.replace(String.valueOf(c), "");
}
String sourcePath = filePath;
filePath = pdfPath + fileName;
FileUploadUtils.copy(sourcePath, filePath);
}
String s = FileUploadUtils.convertToPDF(filePath,pdfPath);
log.info("生成预览地址:{}",s);
String rename = FileUploadUtils.changeSuffix(id, FileUploadUtils.PDF);
log.info("根据id生成预览文件名:{}",rename);
File file = new File(s);
log.info("预览文件:{}",file);
File ff = FileUtil.rename(file, rename, true);
log.info("预览文件改名后:{}",ff);
log.info("生成的pdf文件路径:{},修改后路径:{}",s,ff.getPath());
}
}
package com.cloud.user.util;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.util.RuntimeUtil;
import cn.hutool.system.OsInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.text.DecimalFormat;
@Slf4j
public class FileUploadUtils {
/**
* 点
*/
public static final String DOT = ".";
/**
* 直接预览的文件类型(不通过LibreOffice转换)
*/
public static final String PDF = "pdf";
private static String transfer(MultipartFile file, File targetFile) {
try {
file.transferTo(targetFile);
} catch (IllegalStateException | IOException e) {
e.printStackTrace();
}
return targetFile.getPath();
}
private static void mkParentFile(File targetFile) {
if (!targetFile.getParentFile().exists()) {
targetFile.getParentFile().mkdirs();
}
}
public static String readableFileSize(long size) {
if (size <= 0) {
return "0";
}
final String[] units = new String[]{"B", "KB", "MB", "GB", "TB"};
int digitGroups = (int) (Math.log10(size) / Math.log10(1024));
return new DecimalFormat("#,##0.#").format(size / Math.pow(1024, digitGroups)) + " " + units[digitGroups];
}
/**
* 文件上传
*
* @param file 文件
* @param filePath 文件路径
* @return 文件路径
*/
public static String upload(MultipartFile file, String filePath) {
File targetFile = new File(filePath, file.getOriginalFilename().replaceAll(" ", ""));
mkParentFile(targetFile);
return transfer(file, targetFile);
}
/**
* 处理文件上传
*
* @param file 上传的文件
* @param targetFile 目标文件
* @return 上传文件目录
*/
public static String upload(MultipartFile file, File targetFile) {
mkParentFile(targetFile);
return transfer(file, targetFile);
}
/**
* 批量删除文件或文件夹
*
* @param filePaths 文件路径
*/
public static void deletes(String[] filePaths) {
for (String path : filePaths) {
delete(path);
}
}
/**
* 删除文件或文件夹
*
* @param filePath 文件路径
* @return 成功与否
*/
public static boolean delete(String filePath) {
return FileUtil.del(new File(filePath));
}
/**
* 处理文件上传
*
* @param file 文件
* @param filePath 文件路径
* @param newFileName 新文件名称
* @return 文件大小
*/
public static Long uploadFileContinue(MultipartFile file, String filePath, String newFileName) {
// 记录当前文件大小,用于判断文件是否上传完成
long currentFileLength = 0;
try {
String fileName = file.getOriginalFilename();
RandomAccessFile randomAccessfile;
// 获取文件的扩展名
String ext = "";
if (!StringUtils.isEmpty(fileName)) {
ext = fileName.substring(fileName.lastIndexOf(DOT));
}
File newFile = new File(filePath + newFileName + ext);
if (!newFile.getParentFile().exists()) {
newFile.getParentFile().mkdirs();
}
// 存在文件
if (newFile.exists()) {
randomAccessfile = new RandomAccessFile(newFile, "rw");
} else {
// 不存在文件,根据文件标识创建文件
randomAccessfile = new RandomAccessFile(filePath + newFileName + ext, "rw");
}
// 开始文件传输
InputStream in = file.getInputStream();
if (randomAccessfile.length() < file.getInputStream().available()) {
randomAccessfile.seek(randomAccessfile.length());
in.skip(randomAccessfile.length());
byte[] b = new byte[1024];
int n;
while ((n = in.read(b)) != -1) {
randomAccessfile.write(b, 0, n);
}
}
currentFileLength = randomAccessfile.length();
// 关闭文件
closeRandomAccessFile(randomAccessfile);
} catch (Exception e) {
e.printStackTrace();
}
return currentFileLength;
}
/**
* 关闭随机访问文件
*
* @param randomAccessFile 随机访问的文件
*/
private static void closeRandomAccessFile(RandomAccessFile randomAccessFile) {
if (null != randomAccessFile) {
try {
randomAccessFile.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 转换PDF文件
*
* @param filePath 当前文件路径
* @return PDF文件转换的全路径
*/
public static String convertToPDF(String filePath, String targetPath) {
log.info("filePath:{}", filePath);
log.info("targetPath:{}", targetPath);
String windowsPath = "D:/pdf/upload/";
String command = null;
String pdfPath = null;
if (isLinux()) {
command = "/usr/bin/libreoffice6.1 --headless --invisible --convert-to pdf " + filePath + " --outdir " + targetPath;
pdfPath = targetPath +File.separator + changeSuffix(filePath, PDF);
} else if (isWindows()) {
command = "cmd /c start soffice --headless --invisible --convert-to pdf:writer_pdf_Export " + filePath + " --outdir " + targetPath;
pdfPath = targetPath +File.separator+ changeSuffix(filePath, PDF);
}
System.out.println("pdf command: " + command);
Process exec = RuntimeUtil.exec(command);
try {
long startTime = System.currentTimeMillis();
exec.waitFor();
System.out.println("等待时长:" + (System.currentTimeMillis() - startTime));
} catch (InterruptedException e) {
e.printStackTrace();
}
return pdfPath;
}
/**
* 更改文件后缀
*
* @param filePath 当前文件路径
* @param suffix 后缀
* @return 更改后的文件名
*/
public static String changeSuffix(String filePath, String suffix) {
return getPrefix(filePath) + DOT + suffix;
}
/**
* 判断当前OS的类型
*
* @return boolean
*/
public static boolean isWindows() {
return new OsInfo().isWindows();
}
/**
* 判断当前OS的类型
*
* @return boolean
*/
public static boolean isLinux() {
return new OsInfo().isLinux();
}
/**
* 根据文件名检查文件类型,忽略大小写
*
* @param fileName 文件名,例如hutool.png
* @param extNames 被检查的扩展名数组,同一文件类型可能有多种扩展名,扩展名不带“.”
* @return 是否是指定扩展名的类型
*/
public static boolean isType(String fileName, String... extNames) {
return FileNameUtil.isType(fileName, extNames);
}
/**
* 获取文件名(包含后缀名)
*
* @return 文件名
*/
public static String getName(String filePath) {
return FileNameUtil.getName(filePath);
}
/**
* 获取前缀名
*
* @return 文件前缀名
*/
public static String getPrefix(String filePath) {
return FileNameUtil.getPrefix(filePath);
}
/**
* 获取后缀名(不包括 ".")
*
* @return 文件后缀名
*/
public static String getSuffix(String filePath) {
return FileNameUtil.getSuffix(filePath);
}
/**
* 文件复制(不覆盖目标文件)
*
* @param srcPath 源文件
* @param destPath 目标文件
*/
public static File copy(String srcPath, String destPath) {
return FileUtil.copy(srcPath, destPath, false);
}
// public static void main(String[] args) {
// copy("C:\\Users\\lenovo\\Desktop\\123","C:\\Users\\lenovo\\Desktop\\456");
// }
/**
* 取得OS的文件路径的分隔符(取自系统属性:<code>file.separator</code>)。
*
* <p>
* 例如:Unix为<code>"/"</code>,Windows为<code>"\\"</code>。
* </p>
*
* @return 属性值,如果不能取得(因为Java安全限制)或值不存在,则返回<code>null</code>。
*/
public static String getFileSeparator() {
return new OsInfo().getFileSeparator();
}
public static void downloadFile(HttpServletRequest request, HttpServletResponse response, InputStream inputStream, String fileName) {
try (OutputStream os = response.getOutputStream();
BufferedInputStream bis = new BufferedInputStream(inputStream);
BufferedOutputStream bos = new BufferedOutputStream(os)) {
// 处理下载文件名的乱码问题(根据浏览器的不同进行处理)
if (request.getHeader("User-Agent").toLowerCase().indexOf("firefox") > 0) {
fileName = new String(fileName.getBytes("GB2312"), "ISO-8859-1");
} else {
// 对文件名进行编码处理中文问题
fileName = java.net.URLEncoder.encode(fileName, "UTF-8");// 处理中文文件名的问题
fileName = new String(fileName.getBytes("UTF-8"), "GBK");// 处理中文文件名的问题
}
response.reset();
response.setCharacterEncoding("UTF-8");
response.setContentType("application/x-msdownload");// 不同类型的文件对应不同的MIME类型
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
int bytesRead = 0;
byte[] buffer = new byte[4096];
while ((bytesRead = bis.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
bos.flush();
}
} catch (Exception ex) {
throw new RuntimeException(ex.getMessage());
}
}
/**
* 删除单个文件
*
* @param sPath 被删除文件的文件名
* @return 单个文件删除成功返回true,否则返回false
*/
public static boolean deleteFile(String sPath) {
boolean flag = false;
File file = new File(sPath);
// 路径为文件且不为空则进行删除
if (file.isFile() && file.exists()) {
file.delete();
flag = true;
}
return flag;
}
/**
* 删除目录(文件夹)以及目录下的文件
*
* @param path 被删除目录的文件路径
* @return 目录删除成功返回true,否则返回false
*/
public static boolean deleteDirectory(String path) {
File dirFile = new File(path);
// 如果dir对应的文件不存在,或者不是一个目录,则退出
if (!dirFile.exists() || !dirFile.isDirectory()) {
return false;
}
boolean flag = true;
// 删除文件夹下的所有文件(包括子目录)
File[] files = dirFile.listFiles();
for (int i = 0; i < files.length; i++) {
if (files[i].isFile()) {// 删除子文件
flag = deleteFile(files[i].getAbsolutePath());
if (!flag) {
break;
}
} else {// 删除子目录
flag = deleteDirectory(files[i].getAbsolutePath());
if (!flag) {
break;
}
}
}
if (!flag) {
return false;
}
if (dirFile.delete()) {// 删除当前目录
return true;
} else {
return false;
}
}
public static void chown(String userName, String path) {
String run = RuntimeUtils.execute("id -u " + userName);
String run2 = RuntimeUtils.execute("id -g " + userName);
RuntimeUtils.execute("chown -R " + run + ":" + run2 + " " + path);
}
}
测试
http://47.94.226.83:9000/uploader/uploadFiles
日志
查看生成的预览文件
下载 看是否乱码 不乱码 这是我打的马赛克