In recent work has involved file upload function, limit the demand requirement documents for 1 g, if directly to upload file on the front end, there will be a long time to wait, if the server memory, memory directly, at this time we can be resolved through the breakpoint continuingly, front we achieved by WebUploader and upload file segmentation, React is the language of the backend, we use SpringBoot to implement file receiving and assembly functions, below I list the main functions of the backend code (see the download address at the end of the article for complete code).
First, front-end code
Since WebUploader relies on Jquery, we should have introduced Jquery in the beginning. The front-end scaffolding we used was Ant Design, so I introduced it in SRC /pages/document.ejs file with the following code:
< script type = "text/javascript" SRC = "< % = context. The config. The manifest. BasePath + 'jquery - 1.11.1. Min. Js' % >" > < / script >Copy the code
After introduction, the illustration is as follows:
Then we register three events in the front-end code, namely before-send-file, before-send and after-send-file. These three hooks are respectively executed before sending the file (before uploading the file, triggering once) and before sending the request (before uploading the file into blocks). Trigger multiple times), after file upload (execute after block upload is complete, trigger once)
JSX: @/pages/device/index.jsx
<Form.Item
label="Select version pack"
name="file"
>
<div name="file">
<label
id="uploadWrapper"
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'column',
textAlign: 'center',
border: '1px dashed #E5E5E5',
cursor: inputDisabled ? 'default' : 'pointer'}}htmlFor={'softUpload'}
onDrop={(e)= >{ document.getElementById('uploadWrapper').style.border = '1px dashed #E5E5E5'; Document.getelementbyid ('uploadOption').innerhtml = 'Click or drag the file here to upload '; if (! inputDisabled) { inputOnChange(e); } }} onDragOver={(e) => { e.preventDefault(); document.getElementById('uploadWrapper').style.border = '1px dashed #1890FF'; Document.getelementbyid ('uploadOption').innerhtml = 'release mouse '; }} ><input
disabled={inputDisabled}
type="file"
title=""
id={'softUpload'}
multiple={false}
name="file"
style={{ opacity: 0 }}
onChange={(e)= > inputOnChange(e)}
/>
<label
htmlFor={'softUpload'}
style={{ cursor: inputDisabled ? 'default' : 'pointer'}} >
<p style={{ marginBottom: '10px' }}>
<span
style={{
display: 'block',
width: '102px',
height: '30px',
lineHeight: '30px',
margin: '0 auto',
color: '#1890FF',
backgroundColor: '#E7F3FF',
border: '1px solid #E7F3FF'}} >
<UploadOutlined />upload</span>
</p>
<div>
<p id="uploadOption" className="ant-upload-text">Click or drag the file here to upload</p>
<p className="ant-upload-hint">Supported extensions:<span style={{ color: 'red' }}>{' Version package size does not exceed 1GB'}</span>
</p>
</div>
</label>
</label>
<div
style={{
maxWidth: '100% ',whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
overflow: 'hidden',
lineHeight: 1.padding: '3px 0',
marginTop: '13px'}} >
<span>{task && task.file && task.file.name}</span>
</div>
<div style={{ padding: '0 24px 8px 0', width: '100'}} % >
<Progress
showInfo
style={{ display:` ${inputDisabled ? 'block' : 'none` '}}}strokeColor={{
from: '#108ee9',
to: '#108ee9'}}percent={task.progress}
size="small"
/>
</div>
</div>
</Form.Item>
Copy the code
The core code of file input click event in @/pages/ Device /index.jsx is as follows:
const inputOnChange = async (e) => {
e.preventDefault();
e.stopPropagation();
const files = e.target.files || e.dataTransfer.files;
if (files && files[0]) {
const isLt1G = files[0].size / 1024 / 1024 < 1024;
if(! isLt1G) { message.error('Version package size cannot exceed 1GB! ');
return;
}
addToTaskList(files[0]); }};// Update a single task
const updateTask = (task, newProps) = > {
const newTask = Object.assign({}, task, newProps);
setTask(newTask, function (data) {
uploadNext(data);
});
};
// Call the upload interface
const startUpload = (task) = > {
// Initialization state
const uploader = new Uploader({
file: task.file,
onSuccess: (props) = > {
updateTask(task, { progress: 100.status: 'success' });
setCode(props.code);
setMd5(props.md5);
setInputDisabled(false);
message.success(`${props.fileName}File uploaded successfully);
},
onError: ({ msg }) = > {
setInputDisabled(false);
updateTask(task, { progress: 0.status: 'error' });
message.error(msg);
},
onProgress: ({ file, percentage }) = > {
const progress = (parseInt(percentage.toFixed(4) * 10000.10) - 1) / 100;
updateTask(task, { progress, status: 'uploading'}); }}); updateTask(task, {progress: 0.status: 'uploading', uploader });
setInputDisabled(true);
uploader.start();
};
// Start the next upload task
const uploadNext = (task) = > {
// Only one upload task is allowed at a time
if (task.status === 'uploading') return;
if (task.status === 'init') { startUpload(task); }};// Add a task
const addToTaskList = (file) = > {
setTask({ id: new Date().getTime(), file, progress: 0.status: 'init' }, function (data) {
uploadNext(data);
});
};
Copy the code
@/utils/upload.js encapsulates the breakpoint continuation core code as follows:
import request from '@/utils/request';
import WebUploader from '.. /.. /public/webuploader.min';
import { TP_TOKE, BPR_BASE_URL } from '@/utils/constant';
** * uploader = new uploader ({* file: targetFile, * onSuccess: ({ fileName, resourceId, filePath }) => { * }, * onError: ({ msg }) => { * }, * onProgress: ({ data, percentage }) => { * }, * }); * * uploader.start(); * ` ` ` *@class Uploader* /
class Uploader {
constructor({ file, onSuccess, onError, onProgress }) {
// const files = e.target.files || e.dataTransfer.files;
// Convert to an internal WebUploader file object
this.file = new WebUploader.File(new WebUploader.Lib.File(WebUploader.guid('rt_'), file));
this.onSuccess = props= > {
this.clean();
if (onSuccess) onSuccess(props);
};
this.onError = props= > {
this.clean();
if (onError) onError(props);
};
this.onProgress = onProgress;
this.uploader = null;
}
init = () = > {
WebUploader.Uploader.register({
name: 'webUploaderHookCommand'.'before-send-file': 'beforeSendFile'.'before-send': 'beforeSend'.'after-send-file': 'afterSendFile'}, {beforeSendFile: file= > {
const task = new WebUploader.Deferred();
this.fileName = file.name;
this.fileSize = file.size;
this.mimetype = file.type;
this.fileExt = file.ext;
(new WebUploader.Uploader())
.md5File(file, 0.10 * 1024 * 1024 * 1024 * 1024).progress(percentage= > { })
.then(val= > {
this.fileMd5 = val;
const url = `${BPR_BASE_URL}/register`;
const data = {
fileMd5: this.fileMd5,
fileName: file.name,
fileSize: file.size,
mimetype: file.type,
fileExt: file.ext,
};
request(url, {
method: 'post',
data,
}).then(res= > {
console.log('register', res);
if (res && res.status === 1) {
task.resolve();
} else if (res && res.data && res.code === 103404) {
// The file is uploaded
this.onSuccess({
fileName: this.fileName,
resourceId: res.data.resId,
filePath: res.data.filePath,
});
task.reject();
} else{ file.statusText = res && res.message; task.reject(); }}); });return task.promise();
},
beforeSend: block= > {
console.log('beforeSend');
const task = new WebUploader.Deferred();
const url = `${BPR_BASE_URL}/checkChunk`;
const data = {
fileMd5: this.fileMd5,
chunk: block.chunk,
chunkSize: block.end - block.start,
};
request(url, {
method: 'post',
data,
}).then(res= > {
console.log('checkChunk', res);
if (res && res.data === true) {
task.reject(); // Upload is skipped if fragments exist
} else{ task.resolve(); }});this.uploader.options.formData.fileMd5 = this.fileMd5;
this.uploader.options.formData.chunk = block.chunk;
return task.promise();
},
afterSendFile: () = > {
console.log('start afterSendFile');
const task = new WebUploader.Deferred();
const url = `${BPR_BASE_URL}/mergeChunks`;
const data = {
fileMd5: this.fileMd5,
fileName: this.fileName,
fileSize: this.fileSize,
mimetype: this.mimetype,
fileExt: this.fileExt,
};
request(url, {
method: 'post',
data,
}).then(res= > {
console.log('mergeChunks', res);
if (res && res.status === 1 && res.data && res.data.resId) {
task.resolve();
this.onSuccess({
fileName: this.fileName,
resourceId: res.data.resId,
filePath: res.data.filePath,
});
} else {
task.reject();
this.onError({ msg: 'Failed to merge file'}); }}); }}); } clean =() = > {
if (this.uploader) {
WebUploader.Uploader.unRegister('webUploaderHookCommand');
}
}
start = () = > {
if (!this.uploader) {
this.init();
}
/ / instantiate
this.uploader = WebUploader.create({
server: BPR_BASE_URL,
chunked: true.chunkSize: 1024 * 1024 * 5.chunkRetry: 1.threads: 3.duplicate: true.formData: { // Upload the data carried with the SHARded HTTP request
appid: '1'.token: localStorage.getItem(TP_TOKE),
methodname: 'breakpointRenewal',}});// This method is called when a fragment is successfully uploaded
this.uploader.on('uploadProgress'.(data, percentage) = > {
console.log('uploadProgress');
this.onProgress({ data, percentage });
});
this.uploader.on('error'.err= > {
this.onError({ msg: 'Error uploading, please try again' });
});
this.uploader.addFiles(this.file);
this.uploader.upload();
}
cancel = () = > {
console.log('call cancel');
this.uploader.stop(true);
this.uploader.destroy();
console.log('getStats'.this.uploader.getStats()); }}export default Uploader;
Copy the code
The constant used in the above code is defined in the @/utils/constant file as follows:
const constants = {
BPR_BASE_URL: '/ v1.0 / sys/admin/files/breakpointRenewal'};module.exports = constants;
Copy the code
That’s the end of the front-end code.
Second, back-end code
1, ResponseResult code
/ * * *@Classname: com.openailab.oascloud.common.model.ResponseResult
* @Description: Returns a unified result *@Author: zxzhang
* @Date: 2019/6/26 * /
@Component
public class ResponseResult implements Serializable {
private static final long serialVersionUID = 5836869421731990598L;
/** * State description */
@Getter
private String message;
/** * returns data */
@Getter
private Object data;
/**
* 响应码
*/
@Getter
private int code;
/** * Status (0: failed, 1: succeeded) */
@Getter
private int status;
/** * total number of entries */
@Getter
private Integer total;
public ResponseResult(a) {}public ResponseResult(int status, Object data) {
this.status = status;
this.data = data;
}
public ResponseResult(int status, String message, Object data) {
this.status = status;
this.message = message;
this.data = data;
}
public ResponseResult(String message, Object data, int code, int status) {
this.message = message;
this.data = data;
this.code = code;
this.status = status;
}
public ResponseResult(String message, Object data, int code, int status, Integer total) {
this.message = message;
this.data = data;
this.code = code;
this.status = status;
this.total = total;
}
public static ResponseResult fail(String msg) {
if (StringUtils.isEmpty(msg)) {
return new ResponseResult(ResponseEnum.RESPONSE_CODE_FAIL.getMsg(), null, ResponseEnum.RESPONSE_CODE_FAIL.getCode(), CommonConst.RESPONSE_FAIL);
} else {
return new ResponseResult(msg, null, ResponseEnum.RESPONSE_CODE_FAIL.getCode(), CommonConst.RESPONSE_FAIL); }}public static ResponseResult fail(int code, String msg) {
return new ResponseResult(msg, null, code, CommonConst.RESPONSE_FAIL);
}
public static ResponseResult fail(ResponseEnum responseEnum, Object obj) {
return new ResponseResult(responseEnum.getMsg(), obj, responseEnum.getCode(), 0);
}
public static ResponseResult success(Object data) {
return new ResponseResult(ResponseEnum.RESPONSE_CODE_SUCCESS.getMsg(), data, ResponseEnum.RESPONSE_CODE_SUCCESS.getCode(), CommonConst.RESPONSE_SUCCESS);
}
public static ResponseResult success(Object data, int code, String message) {
return new ResponseResult(message, data, code, CommonConst.RESPONSE_SUCCESS);
}
public ResponseResult setMessage(String message) {
this.message = message;
return this;
}
public ResponseResult setData(Object data) {
this.data = data;
return this;
}
public ResponseResult setStatus(int status) {
this.status = status;
return this;
}
public ResponseResult setCode(int code) {
this.code = code;
return this;
}
public ResponseResult setTotal(Integer total) {
this.total = total;
return this; }}Copy the code
2. FileController code
/ * * *@description: File management -Controller *@author: zhangzhixiang
* @createDate: 2019/12/9
* @version: 1.0 * /
@RestController
@ RequestMapping ("/v1.0 / sys/admin/files "),
public class FileController {
private static Logger LOG = LoggerFactory.getLogger(FileController.class);
@Autowired
private IFileService fileService;
@Autowired
private IUserService userService;
/** ** *@param file
* @param fileMd5
* @param chunk
* @return com.openailab.oascloud.common.model.ResponseResult
* @author zxzhang
* @date2020/1/13 * /
@PostMapping(value = "/breakpointRenewal", produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseResult breakpointRenewal(@RequestPart("file") MultipartFile file,
@RequestParam("fileMd5") String fileMd5,
@RequestParam("chunk") Integer chunk) {
try {
return fileService.breakpointRenewal(file, fileMd5, chunk);
} catch (Exception e) {
LOG.error("********FileController->breakpointRenewal throw Exception.fileMd5:{},chunk:{}********", fileMd5, chunk, e);
}
return ResponseResult.fail(null);
}
/** * register **@param fileMd5
* @param fileName
* @param fileSize
* @param mimetype
* @param fileExt
* @return com.openailab.oascloud.common.model.ResponseResult
* @author zxzhang
* @date2020/1/13 * /
@PostMapping(value = "/breakpointRenewal/register")
public ResponseResult breakpointRegister(@RequestParam("fileMd5") String fileMd5,
@RequestParam("fileName") String fileName,
@RequestParam("fileSize") Long fileSize,
@RequestParam("mimetype") String mimetype,
@RequestParam("fileExt") String fileExt) {
try {
return fileService.breakpointRegister(fileMd5, fileName, fileSize, mimetype, fileExt);
} catch (Exception e) {
LOG.error("********FileController->breakpointRegister throw Exception.fileMd5:{},fileName:{}********", fileMd5, fileName, e);
}
return ResponseResult.fail(null);
}
/** * check whether the partition exists **@param fileMd5
* @param chunk
* @param chunkSize
* @return com.openailab.oascloud.common.model.ResponseResult
* @author zxzhang
* @date2020/1/10 * /
@PostMapping(value = "/breakpointRenewal/checkChunk")
public ResponseResult checkChunk(@RequestParam("fileMd5") String fileMd5,
@RequestParam("chunk") Integer chunk,
@RequestParam("chunkSize") Integer chunkSize) {
try {
return fileService.checkChunk(fileMd5, chunk, chunkSize);
} catch (Exception e) {
LOG.error("********FileController->breakpointRenewal throw Exception.fileMd5:{},chunk:{}********", fileMd5, chunk, e);
}
return ResponseResult.fail(null);
}
/** * Merge file blocks **@param fileMd5
* @param fileName
* @param fileSize
* @param mimetype
* @param fileExt
* @return com.openailab.oascloud.common.model.ResponseResult
* @author zxzhang
* @date2020/1/11 * /
@PostMapping(value = "/breakpointRenewal/mergeChunks")
public ResponseResult mergeChunks(@RequestParam("fileMd5") String fileMd5,
@RequestParam("fileName") String fileName,
@RequestParam("fileSize") Long fileSize,
@RequestParam("mimetype") String mimetype,
@RequestParam("fileExt") String fileExt,
@RequestParam("token") String token) {
try {
LoginUserInfo user = userService.getLoginUser(token);
return fileService.mergeChunks(fileMd5, fileName, fileSize, mimetype, fileExt, user);
} catch (Exception e) {
LOG.error("********FileController->breakpointRenewal throw Exception.fileMd5:{},fileName:{}********", fileMd5, fileName, e);
}
return ResponseResult.fail(null); }}Copy the code
3. IFileService code
/ * * *@description: file management -Interface *@author: zhangzhixiang
* @createDate: 2019/12/9
* @version: 1.0 * /
public interface IFileService {
/** * register **@param fileMd5
* @param fileName
* @param fileSize
* @param mimetype
* @param fileExt
* @return com.openailab.oascloud.common.model.ResponseResult
* @author zxzhang
* @date2020/1/10 * /
ResponseResult breakpointRegister(String fileMd5, String fileName, Long fileSize, String mimetype, String fileExt);
/** ** *@param file
* @return com.openailab.oascloud.common.model.ResponseResult
* @author zxzhang
* @date2019/12/9 * /
ResponseResult breakpointRenewal(MultipartFile file, String fileMd5, Integer chunk);
/** * check whether the partition exists **@param fileMd5
* @param chunk
* @param chunkSize
* @return com.openailab.oascloud.common.model.ResponseResult
* @author zxzhang
* @date2020/1/10 * /
ResponseResult checkChunk(String fileMd5, Integer chunk, Integer chunkSize);
/** * Merge file blocks **@param fileMd5
* @param fileName
* @param fileSize
* @param mimetype
* @param fileExt
* @return com.openailab.oascloud.common.model.ResponseResult
* @author zxzhang
* @date2020/1/11 * /
ResponseResult mergeChunks(String fileMd5, String fileName, Long fileSize, String mimetype, String fileExt, LoginUserInfo user);
}
Copy the code
FileServiceImpl code
/ * * *@description: file management -service *@author: zhangzhixiang
* @createDate: 2019/12/9
* @version: 1.0 * /
@Service
public class FileServiceImpl implements IFileService {
private final static Logger LOG = LoggerFactory.getLogger(FileServiceImpl.class);
private static final SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");
@Autowired
private FileDao fileDao;
@Autowired
private BootstrapConfig bootstrapConfig;
@Autowired
private FileManagementHelper fileManagementHelper;
@Autowired
private PageObjUtils pageObjUtils;
@Autowired
private RedisDao redisDao;
private String getUploadPath(a) {
return bootstrapConfig.getFileRoot() + bootstrapConfig.getUploadDir() + "/";
}
private String getFileFolderPath(String fileMd5) {
return getUploadPath() + fileMd5.substring(0.1) + "/" + fileMd5.substring(1.2) + "/";
}
private String getFilePath(String fileMd5, String fileExt) {
return getFileFolderPath(fileMd5) + fileMd5 + "." + fileExt;
}
private String getFileRelativePath(String fileMd5, String fileExt) {
return bootstrapConfig.getUploadDir() + "/" + fileMd5.substring(0.1) + "/" + fileMd5.substring(1.2) + "/" + fileMd5 + "." + fileExt;
}
private String getChunkFileFolderPath(String fileMd5) {
return bootstrapConfig.getFileRoot() + bootstrapConfig.getBreakpointDir() + "/" + fileMd5 + "/";
}
@Override
public ResponseResult breakpointRegister(String fileMd5, String fileName, Long fileSize, String mimetype, String fileExt) {
Map<String, String> ret = Maps.newHashMap();
// Check whether the file exists on disk
String fileFolderPath = this.getFileFolderPath(fileMd5);
String filePath = this.getFilePath(fileMd5, fileExt);
File file = new File(filePath);
boolean exists = file.exists();
// Check whether the file exists in PostgreSQL (file with unique identifier fileMd5)
ResourceBO resourceBO = new ResourceBO();
resourceBO.setFileMd5(fileMd5);
resourceBO.setIsDelete(0);
List<ResourceBO> resourceBOList = fileDao.selectResourceByCondition(resourceBO);
if (exists && resourceBOList.size() > 0) {
ResId, filePath, resId, filePath, resId, filePath
resourceBO = resourceBOList.get(0);
ret.put("filePath", resourceBO.getFilePath());
ret.put("resId", String.valueOf(resourceBO.getResourceId()));
return ResponseResult.fail(ResponseEnum.RESPONSE_CODE_BREAKPOINT_RENEVAL_REGISTRATION_ERROR, ret);
}
// If it exists on disk but not in database, generate resource record and save it to redis
if (resourceBOList.size() == 0) {
// The first breakpoint file needs to create a new resource record and return redId to be stored in Redis
resourceBO.setType(fileManagementHelper.judgeDocumentType(fileExt));
resourceBO.setStatus(TranscodingStateEnum.UPLOAD_NOT_COMPLETED.getCode());
resourceBO.setFileSize(fileSize);
resourceBO.setFileMd5(fileMd5);
resourceBO.setFileName(fileName);
resourceBO.setCreateDate(new Date());
resourceBO.setIsDelete(0);
final Integer resourceId = fileDao.addResource(resourceBO);
resourceBO.setResourceId(resourceId);
redisDao.set(RedisPrefixConst.BREAKPOINT_PREFIX + fileMd5, JSONObject.toJSONString(resourceBO), RedisPrefixConst.EXPIRE);
}
// If it does not exist in redis but exists in the database, store it in redis
String breakpoint = redisDao.get(RedisPrefixConst.BREAKPOINT_PREFIX + fileMd5);
if (StringUtils.isEmpty(breakpoint) && resourceBOList.size() > 0) {
resourceBO = resourceBOList.get(0);
redisDao.set(RedisPrefixConst.BREAKPOINT_PREFIX + fileMd5, JSONObject.toJSONString(resourceBO), RedisPrefixConst.EXPIRE);
}
// If the file does not exist, check whether the file directory exists
File fileFolder = new File(fileFolderPath);
if(! fileFolder.exists()) {// The directory was not created (the directory was created based on the MD5 value from the front end)
fileFolder.mkdirs();
}
return ResponseResult.success(null);
}
@Override
public ResponseResult breakpointRenewal(MultipartFile file, String fileMd5, Integer chunk) {
Map<String, String> ret = Maps.newHashMap();
// Check whether the block directory exists
String chunkFileFolderPath = this.getChunkFileFolderPath(fileMd5);
File chunkFileFolder = new File(chunkFileFolderPath);
if(! chunkFileFolder.exists()) { chunkFileFolder.mkdirs(); }// Upload file input stream
File chunkFile = new File(chunkFileFolderPath + chunk);
try (InputStream inputStream = file.getInputStream(); FileOutputStream outputStream = new FileOutputStream(chunkFile)) {
IOUtils.copy(inputStream, outputStream);
// Check whether there are fileMd5 block records in Redis (resId)
String breakpoint = redisDao.get(RedisPrefixConst.BREAKPOINT_PREFIX + fileMd5);
ResourceBO resourceBO = new ResourceBO();
if(! StringUtils.isEmpty(breakpoint)) {// The existence of block records indicates that the resource is being uploaded. The resId corresponding to fileMd5 is returned directly, and the resource records will not be created repeatedly
resourceBO = JSONObject.parseObject(breakpoint, ResourceBO.class);
ret.put("resId", String.valueOf(resourceBO.getResourceId())); }}catch (IOException e) {
e.printStackTrace();
}
return ResponseResult.success(ret);
}
@Override
public ResponseResult checkChunk(String fileMd5, Integer chunk, Integer chunkSize) {
// Check whether the partitioned file exists
String chunkFileFolderPath = this.getChunkFileFolderPath(fileMd5);
// The path where the partition is located + the index of the partition can locate the specific partition
File chunkFile = new File(chunkFileFolderPath + chunk);
if (chunkFile.exists() && chunkFile.length() == chunkSize) {
return ResponseResult.success(true);
}
return ResponseResult.success(false);
}
@Override
public ResponseResult mergeChunks(String fileMd5, String fileName, Long fileSize, String mimetype, String fileExt, LoginUserInfo user) {
FileClient fileClient = ClientFactory.createClientByType(bootstrapConfig.getFileClientType());
String chunkFileFolderPath = this.getChunkFileFolderPath(fileMd5);
File chunkFileFolder = new File(chunkFileFolderPath);
File[] files = chunkFileFolder.listFiles();
final String filePath = this.getFilePath(fileMd5, fileExt);
File mergeFile = new File(filePath);
List<File> fileList = Arrays.asList(files);
// 1. Merge blocks
mergeFile = this.mergeFile(fileList, mergeFile);
if (mergeFile == null) {
return ResponseResult.fail(ResponseEnum.RESPONSE_CODE_MERGE_FILE_ERROR, null);
}
// 2. Check whether the MD5 file is consistent with the incoming one
boolean checkResult = this.checkFileMd5(mergeFile, fileMd5);
if(! checkResult) {return ResponseResult.fail(ResponseEnum.RESPONSE_CODE_VERIFY_FILE_ERROR, null);
}
// 3. Delete all blocks of the file
FileUtil.deleteDir(chunkFileFolderPath);
// Get file block records in Redis
String breakpoint = redisDao.get(RedisPrefixConst.BREAKPOINT_PREFIX + fileMd5);
if (StringUtils.isEmpty(breakpoint)) {
return ResponseResult.fail("File block does not exist");
}
ResourceBO resourceBO = JSONObject.parseObject(breakpoint, ResourceBO.class);
// Delete redis blocks
redisDao.del(RedisPrefixConst.BREAKPOINT_PREFIX + fileMd5);
// 6
ret.put("filePath", getFileRelativePath(fileMd5, fileExt));
ret.put("resId", String.valueOf(resourceBO.getResourceId()));
return ResponseResult.success(ret);
}
/** * merge files **@param chunkFileList
* @param mergeFile
* @return java.io.File
* @author zxzhang
* @date2020/1/11 * /
private File mergeFile(List<File> chunkFileList, File mergeFile) {
try {
// Yes Delete no create
if (mergeFile.exists()) {
mergeFile.delete();
} else {
mergeFile.createNewFile();
}
/ / sorting
Collections.sort(chunkFileList, (o1, o2) -> {
if (Integer.parseInt(o1.getName()) > Integer.parseInt(o2.getName())) {
return 1;
}
return -1;
});
byte[] b = new byte[1024];
RandomAccessFile writeFile = new RandomAccessFile(mergeFile, "rw");
for (File chunkFile : chunkFileList) {
RandomAccessFile readFile = new RandomAccessFile(chunkFile, "r");
int len = -1;
while((len = readFile.read(b)) ! = -1) {
writeFile.write(b, 0, len);
}
readFile.close();
}
writeFile.close();
return mergeFile;
} catch (IOException e) {
e.printStackTrace();
return null; }}/** * Verify file MD5 **@param mergeFile
* @param md5
* @return boolean
* @author zxzhang
* @date2020/1/11 * /
private boolean checkFileMd5(File mergeFile, String md5) {
try {
// get the MD5 file
FileInputStream inputStream = new FileInputStream(mergeFile);
String md5Hex = DigestUtils.md5Hex(inputStream);
if (StringUtils.equalsIgnoreCase(md5, md5Hex)) {
return true; }}catch (Exception e) {
e.printStackTrace();
}
return false;
}
/** * obtain the file suffix **@param fileName
* @return java.lang.String
* @author zxzhang
* @date2019/12/10 * /
public String getExt(String fileName) {
return fileName.substring(fileName.lastIndexOf(".") + 1);
}
/** * Get the file directory **@param filePath
* @return java.lang.String
* @author zxzhang
* @date2019/12/10 * /
public String getFileDir(String filePath) {
return filePath.substring(0, filePath.lastIndexOf(BootstrapConst.PATH_SEPARATOR));
}
/** * get the file name **@param filePath
* @return java.lang.String
* @author zxzhang
* @date2019/12/10 * /
public String getFileName(String filePath) {
return filePath.substring(filePath.lastIndexOf(BootstrapConst.PATH_SEPARATOR) + 1, filePath.lastIndexOf(".")); }}Copy the code
5. FileUtil code
/ * * *@description:
* @author: zhangzhixiang
* @createDate: 2020/1/7
* @version: 1.0 * /
public class FileUtil {
private static final Logger LOG = LoggerFactory.getLogger(FileUtil.class);
/** * Clear all files in the folder **@param path
* @return boolean
* @author zxzhang
* @date2020/1/7 * /
public static boolean deleteDir(String path) {
File file = new File(path);
if(! file.exists()) {// Check whether the directory to be deleted exists
return false;
}
String[] content = file.list();// Get all files and folders in the current directory
for (String name : content) {
File temp = new File(path, name);
if (temp.isDirectory()) {// Check if it is a directory
deleteDir(temp.getAbsolutePath());// Delete the contents of the directory recursively
temp.delete();// Delete an empty directory
} else {
if(! temp.delete()) {// Delete the file directly
LOG.error("******** file deletion failed, file: {}********"+ name); }}}return true;
}
/** * Copy a single file **@paramOldPath String Path of the original file, for example, c:/ FQF *@paramNewPath String Path after replication, for example, f:/ FQF /ff *@return boolean
*/
public static void copyFile(String oldPath, String newPath) {
try {
int bytesum = 0;
int byteread = 0;
File oldfile = new File(oldPath);
if (oldfile.exists()) { // If the file exists
InputStream inStream = new FileInputStream(oldPath); // Read the original file
// The newFilePath folder does not exist
File newParentFile = new File(newPath).getParentFile();
if(! newParentFile.exists()) { newParentFile.mkdirs(); } FileOutputStream fs =new FileOutputStream(newPath);
byte[] buffer = new byte[1444];
int length;
while((byteread = inStream.read(buffer)) ! = -1) {
bytesum += byteread; // Number of bytes File size
System.out.println(bytesum);
fs.write(buffer, 0, byteread); } inStream.close(); }}catch (Exception e) {
LOG.error("******** Error copying single file ********"); e.printStackTrace(); }}/** * copy the entire folder **@paramOldPath String Path of the original file, for example, c:/ FQF *@paramNewPath String Path after replication, for example, f:/ FQF /ff *@return boolean
*/
public static void copyFolder(String oldPath, String newPath) {
try {
// The newFilePath folder does not exist
File newParentFile = new File(newPath).getParentFile();
if(! newParentFile.exists()) { newParentFile.mkdirs(); } File a =new File(oldPath);
String[] file = a.list();
File temp = null;
for (int i = 0; i < file.length; i++) {
if (oldPath.endsWith(File.separator)) {
temp = new File(oldPath + file[i]);
} else {
temp = new File(oldPath + File.separator + file[i]);
}
if (temp.isFile()) {
FileInputStream input = new FileInputStream(temp);
FileOutputStream output = new FileOutputStream(newPath + "/" +
(temp.getName()).toString());
byte[] b = new byte[1024 * 5];
int len;
while((len = input.read(b)) ! = -1) {
output.write(b, 0, len);
}
output.flush();
output.close();
input.close();
}
if (temp.isDirectory()) {// If it is a subfolder
copyFolder(oldPath + "/" + file[i], newPath + "/"+ file[i]); }}}catch (Exception e) {
LOG.error("******** Error copying contents of entire folder ********"); e.printStackTrace(); }}/** * Get the MD5 value of a file **@param file
* @return java.lang.String
* @author zxzhang
* @date2020/3/23 * /
public static String getMD5(MultipartFile file) {
InputStream fileInputStream = null;
try {
MessageDigest MD5 = MessageDigest.getInstance("MD5");
fileInputStream = file.getInputStream();
byte[] buffer = new byte[8192];
int length;
while((length = fileInputStream.read(buffer)) ! = -1) {
MD5.update(buffer, 0, length);
}
return new String(Hex.encodeHex(MD5.digest()));
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
try {
if(fileInputStream ! =null) { fileInputStream.close(); }}catch(IOException e) { e.printStackTrace(); }}}/** * Get the MD5 value of a file **@param file
* @return java.lang.String
* @author zxzhang
* @date2020/3/23 * /
public static String getMD5(File file) {
try(FileInputStream fileInputStream = new FileInputStream(file)) {
MessageDigest MD5 = MessageDigest.getInstance("MD5");
byte[] buffer = new byte[8192];
int length;
while((length = fileInputStream.read(buffer)) ! = -1) {
MD5.update(buffer, 0, length);
}
return new String(Hex.encodeHex(MD5.digest()));
} catch (Exception e) {
e.printStackTrace();
return null; }}/** * find the MD5 value of a string **@param target
* @return java.lang.String
* @author zxzhang
* @date2020/3/23 * /
public static String MD5(String target) {
returnDigestUtils.md5Hex(target); }}Copy the code
6. FileManagementHelper code
/ * * *@description:
* @author: zhangzhixiang
* @createDate: 2019/12/11
* @version: 1.0 * /
@Component
public class FileManagementHelper {
private static final Logger LOG = LoggerFactory.getLogger(FileManagementHelper.class);
@Autowired
private BootstrapConfig bootstrapConfig;
/** * Determine the file type based on the file suffix **@param ext
* @return java.lang.Integer
* @author zxzhang
* @date2019/12/10 * /
public Integer judgeDocumentType(String ext) {
/ / video class
if(VedioEnum.containKey(ext) ! =null) {
return ResourceTypeEnum.VIDEO.getCode();
}
/ / picture
if(ImageEnum.containKey(ext) ! =null) {
return ResourceTypeEnum.IMAGE.getCode();
}
/ / document class
if(DocumentEnum.containKey(ext) ! =null) {
return ResourceTypeEnum.FILE.getCode();
}
/ / unknown
return ResourceTypeEnum.OTHER.getCode();
}
/** * Generates a random file name **@param ext
* @return java.lang.String
* @author zxzhang
* @date2019/12/10 * /
public static String createFileName(String ext) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
return simpleDateFormat.format(new Date()) + (int) (Math.random() * 900 + 100) + ext;
}
/** * obtain the file suffix **@param fileName
* @return java.lang.String
* @author zxzhang
* @date2019/12/10 * /
public String getExt(String fileName) {
return fileName.substring(fileName.lastIndexOf(".") + 1);
}
/** * Get the file directory **@param filePath
* @return java.lang.String
* @author zxzhang
* @date2019/12/10 * /
public String getFileDir(String filePath) {
return filePath.substring(0, filePath.lastIndexOf(BootstrapConst.PATH_SEPARATOR));
}
/** * get the file name **@param filePath
* @return java.lang.String
* @author zxzhang
* @date2019/12/10 * /
public String getFileName(String filePath) {
return filePath.substring(filePath.lastIndexOf(BootstrapConst.PATH_SEPARATOR) + 1, filePath.lastIndexOf(".")); }}Copy the code
7, PageObjUtils code
/ * * *@Classname: com.openailab.oascloud.um.util.PageObjUtils
* @Description: paging object utility class *@Author: ChenLiang
* @Date: 2019/7/17 * /
@Component
public class PageObjUtils<T> {
public PageVO getPageList(PageInfo<T> personPageInfo) {
PageVO result = new PageVO();
if(personPageInfo ! =null) {
if (!personPageInfo.getList().isEmpty()) {
result.setPageNo(personPageInfo.getPageNum());
result.setPageSize(personPageInfo.getPageSize());
result.setTotal(Integer.valueOf(String.valueOf(personPageInfo.getTotal())));
result.setItems(personPageInfo.getList());
}
}
returnresult; }}Copy the code
8. RedisDao code
/ * * *@Classname: com.openailab.oascloud.datacenter.api.IRedisApi
* @Description: Redis API
* @Author: zxzhang
* @Date: 2019/7/1 * /
@FeignClient(ServiceNameConst.OPENAILAB_DATA_CENTER_SERVICE)
public interface RedisDao {
/ * * *@api{POST} /redis/set Normal cache is put and set expiration time *@apiGroup Redis
* @apiVersion0.1.0 from *@apiParam* {String} key key@apiParam* {String} the value value@apiParam{long} expire Expire time */
@PostMapping("/redis/set")
ResponseResult set(@RequestParam("key") String key, @RequestParam("value") String value, @RequestParam("expire") long expire);
/ * * *@api{POST} /redis/get Common cache fetch *@apiGroup Redis
* @apiVersion0.1.0 from *@apiParam* {String} key key@apiSuccess{String} the value value * /
@PostMapping("/redis/get")
String get(@RequestParam("key") String key);
/ * * *@api{POST} /redis/del Normal cache delete *@apiGroup Redis
* @apiVersion0.1.0 from *@apiParam{String} key key * /
@PostMapping("/redis/del")
ResponseResult del(@RequestParam("key") String key);
/ * * *@api{POST} /redis/hset stores Hash value and sets expiration time *@apiGroup Redis
* @apiVersion0.1.0 from *@apiParam* {String} key key@apiParam{String} * item item@apiParam* {String} the value value@apiParam{long} expire Expire time */
@PostMapping("/redis/hset")
ResponseResult hset(@RequestParam("key") String key, @RequestParam("item") String item, @RequestParam("value") String value, @RequestParam("expire") long expire);
/ * * *@api{POST} /redis/hget gets the Hash value *@apiGroup Redis
* @apiVersion0.1.0 from *@apiParam* {String} key key@apiParam{String} * item item@apiSuccess* {String} the value value@apiSuccessExample{json} Successful Example * {"name":" Zhang SAN ","age":30} */
@PostMapping("/redis/hget")
Object hget(@RequestParam("key") String key, @RequestParam("item") String item);
/ * * *@api{POST} /redis/hdel Deletes the Hash value SaasAppKeyDao *@apiGroup Redis
* @apiVersion0.1.0 from *@apiParam* {String} key key@apiParam{String} * / item item
@PostMapping("/redis/hdel")
ResponseResult hdel(@RequestParam("key") String key, @RequestParam("item") String item);
}
Copy the code
9. BootstrapConfig code
/ * * *@Classname: com.openailab.oascloud.security.common.config.BootstrapConsts
* @Description: Bootstrap *@Author: zxzhang
* @Date: 2019/10/8 * /
@Data
@Configuration
public class BootstrapConfig {
@Value("${file.client.type}")
private String fileClientType;
@Value("${file.root}")
private String fileRoot;
@Value("${file.biz.file.upload}")
private String uploadDir;
@Value("${file.biz.file.download}")
private String downloadDir;
@Value("${file.biz.file.backup}")
private String backupDir;
@Value("${file.biz.file.tmp}")
private String tmpDir;
@Value("${file.biz.file.breakpoint}")
private String breakpointDir;
}
Copy the code
10. And application properties
eureka.instance.instance-id=${spring.application.name}:${server.port} eureka.instance.prefer-ip-address=true Eureka. Client. ServiceUrl. DefaultZone = http://127.0.0.1:32001/eureka/ server port = 32018 spring.application.name=openailab-file-management #file file.client.type = ceph file.root = /usr/local/oas/file file.biz.file.upload = /upload file.biz.file.download = /download file.biz.file.backup = /backup file.biz.file.tmp = /tmp file.biz.file.breakpoint = /breakpoint #ribbon ribbon.ReadTimeout=600000 ribbon.ConnectTimeout=600000 #base The info. The description = file management service [email protected] spring. Servlet. Multipart. Enabled = true spring.servlet.multipart.max-file-size=5120MB spring.servlet.multipart.max-request-size=5120MBCopy the code
Table structure
The field name | annotation | type | The length of the | If required | Whether the primary key |
---|---|---|---|---|---|
id | The primary key ID, sequence (course_resource_id_seq) | int | 32 | is | is |
type | Resource type, 1: video. 2. Picture 3: | int | 2 | is | no |
fileName | The file name | varchar | 100 | is | no |
fileSize | The file size | int | 64 | is | no |
filePath | The file path | varchar | 200 | no | no |
status | 0: no transcoding is required. 1: transcoding is in progress. 2: transcoding is completed | int | 2 | is | no |
createDate | Creation time | timestamp | 0 | is | no |
createUser | Create a user | varchar | 50 | is | no |
isDelete | Deleted or Not: 0 is not deleted, and 1 is deleted | int | 2 | is | no |
userId | The user ID | int | 32 | is | no |
fileMd5 | Unique FILE IDENTIFIER (Unique MD5 identifier of Webupload files) | varchar | 100 | is | no |
File breakpoint continued to upload here to introduce the end, the complete front and back end code here, portal:
WebUploader
Resumable Back-end Code Download (Java)