io流转换为Multipart文件
io流转换为Multipart文件
个人的话是运用到了minio文件服务器保存文件,前端(vue)异步上传文件后,由于要提升用户体验效果,先上传文件到后台服务器,返回视频在文件服务器的link()参数,然后保存该文件到数据库,然后运用java 定时任务之一 @Scheduled注解(如何使用自行www.baidu.com),定时进行在后台转码相关视频文件。运用到的转码工具是FFmpeg.exe。
详细如下:
- 新建一份工具类文件,进行转码操作(亲测转码各参数都有用),个人的话是统一转码为MP4文件。
package org.springblade.common.utils; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springblade.common.config.loadFileConfig;
import org.springblade.common.service.ResourceService;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.tool.api.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.multipart.MultipartFile; import java.io.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List; import static jodd.io.FileUtil.deleteFile; /**
* Created by liuminghui on 2020/12/30 22:14
*
* @annotate:
* @Version 1.0
*/
public class DisposeVideoUtils {
private static final Logger logger = LoggerFactory.getLogger(DisposeVideoUtils.class); // 转码ffmpeg.exe工具路径
private static final String FFMPEG_PATH = "F:\\gugexaizai\\acutvideo\\ffmpeg.exe"; // 转码mencoder.exe工具路径
private static final String MENCODER_PATH = "F:\\gugexaizai\\acutvideo\\mencoder.exe"; // 临时的视频存储路径,转码完成后可删除
private static final String TEMPORARY_VIDEO_PATH = "F:\\Images\\"; // 转码成功mp4视频存放路径 private static final String uploadFolder="F:\\ImagesPath\\"; // 转码后台的视频访问路径
private String videoUrl; // 视频大小
private String size;
private String filerealname;
private String PATH;
// 视频截图路径
private String videoImg; public DisposeVideoUtils() {
} //重构构造方法,传入视频存放路径
public DisposeVideoUtils(String path) {
videoUrl = path;
} //set和get方法传递path
public String getVideoPath() {
return videoUrl;
} public String getSize() {
return size;
} public void setSize(String size) {
this.size = size;
} public void setVideoPath(String path) {
videoUrl = path;
} public String getVideoImg() {
return videoImg;
} public void setVideoImg(String videoImg) {
this.videoImg = videoImg;
} /**
* 转码、截图和删除源文件功能
* @param tempPath 临时源视频文件路径
*/
// public String runConver(String tempPath) {
// logger.info("=================转码过程开始=====================");
// try {
// String targetExtension = ".mp4"; // 设置转换的格式
// boolean isDelSourseFile = true;
// // 转码、截图和删除源文件
// boolean beginConver = beginVideoConver(tempPath, targetExtension, isDelSourseFile);
// if (beginConver) {
// logger.info("=================转码过程彻底结束=====================");
// return videoUrl;
// }
// } catch (Exception e) {
// e.printStackTrace();
// }
// return null;
// } /**
* 视频转码、截图和删除原视频处理
* @param tempPath 临时源视频路径
* @param targetExtension 转码成功的视频后缀名
* @param isDelSourseFile 转换完成后是否删除源文件
* @return
*/
// private boolean beginVideoConver(String tempPath, String targetExtension, boolean isDelSourseFile) {
// File file = new File(tempPath);
// String fileName = file.getName(); //获取文件名+后缀名
// String fileNameSuffix = fileName.substring(fileName.lastIndexOf(".") + 1); // 获取需要转码的文件后缀
// //执行转码机制
// if (process(tempPath, fileNameSuffix, targetExtension)) {
// //删除临时视频或转码原视频
// if (isDelSourseFile) {
// // 删除临时文件
// File fileDelete = new File(TEMPORARY_VIDEO_PATH);
// String[] tempList = fileDelete.list();
// File temp = null;
// for (int i = 0; i < tempList.length; i++) {
// if (TEMPORARY_VIDEO_PATH.endsWith(File.separator)) {
// temp = new File(TEMPORARY_VIDEO_PATH + tempList[i]);
// } else {
// temp = new File(TEMPORARY_VIDEO_PATH + File.separator + tempList[i]);
// }
// if (temp.isFile() || temp.isDirectory()) {
// temp.delete(); // 删除文件夹里面的文件
// }
// }
// }
//
// return true;
// } else {
// return false;
// }
// } /**
* 实际转换视频格式的方法
* @param tempPath 临时源视频文件路径
* @param fileNameSuffix 源视频后缀名
* @param targetExtension 转码成功的视频后缀名
* @return
*/
// private boolean process(String tempPath, String fileNameSuffix, String targetExtension) {
// //先判断视频的类型-返回状态码
// int type = checkVideoSuffix(fileNameSuffix);
// boolean status = false;
//
// //根据状态码处理
// if (type == 0) {
// logger.info("ffmpeg可以转换,统一转为mp4文件");
// String status = processVideoFormat(tempPath, targetExtension);//可以指定转换为什么格式的视频
// } else if (type == 1) {
// //如果type为1,将其他文件先转换为avi,然后在用ffmpeg转换为指定格式
// logger.info("ffmpeg不可以转换,先调用mencoder转码avi");
// String avifilepath = processAVI(tempPath);
//
// if (avifilepath == null){
// // 转码失败--avi文件没有得到
// logger.info("mencoder转码失败,未生成AVI文件");
// return false;
// }else {
// logger.info("生成AVI文件成功,ffmpeg开始转码:");
// String status = processVideoFormat(avifilepath, targetExtension);
// }
// }
// return status; //执行完成返回布尔类型true
// } /**
* 转换为指定格式
* ffmpeg能解析的格式:(asx,asf,mpg,wmv,3gp,mp4,mov,avi,flv等)
* @param oldfilepath 临时源视频文件路径
* @param targetExtension 转码成功的视频后缀名 .xxx
* @return
*/
public String processVideoFormat(String oldfilepath, String targetExtension) {
logger.info("调用了ffmpeg.exe工具"); // 先确保保存转码后的视频的mp4文件夹存在:TRANSCODE_VIDEO_MP4PATH-保存路径
String mp4Path = uploadFolder + Long.toString(new Date().getTime()) + targetExtension;
File tempFile = new File(uploadFolder);
if (tempFile.exists()) {
if (tempFile.isDirectory()) {
logger.info("该转码后的文件夹已存在");
} else {
logger.info("同名的转码后的文件存在,不能创建文件夹");
}
} else {
// 创建目录
if (tempFile.mkdirs()) {
logger.info("创建目录转码后的文件夹:" + uploadFolder + ",成功!");
} else {
logger.info("创建目录转码后的文件夹:" + uploadFolder + ",失败!");
}
}
List<String> commend = new ArrayList<String>();
commend.add(FFMPEG_PATH); // 添加转换工具路径
commend.add("-i"); // 添加参数"-i",该参数指定要转换的文件
commend.add(oldfilepath); // 添加要转换格式的视频文件的路径
commend.add("-vcodec");
commend.add("libx264");
commend.add("-acodec");
commend.add("aac");
commend.add("-ab");
commend.add("128k");
commend.add("-ar");
commend.add("16k");
commend.add("-ac");
commend.add("2");
commend.add("-f"); // 添加参数"-y",该参数指定将覆盖已存在的文件
commend.add("mp4"); // 添加参数"-y",该参数指定将覆盖已存在的文件
commend.add("-y"); // 添加参数"-y",该参数指定将覆盖已存在的文件
commend.add(mp4Path); // 打印命令
StringBuffer test = new StringBuffer();
for (int i = 0; i < commend.size(); i++) {
test.append(commend.get(i) + " ");
}
logger.info("ffmpeg输入的命令:" + test); try {
String suffix = oldfilepath.substring(oldfilepath.lastIndexOf(".") + 1);
if(checkVideoSuffix(suffix)==0){
// 多线程处理加快速度-解决rmvb数据丢失builder名称要相同
ProcessBuilder builder = new ProcessBuilder();
builder.command(commend);
builder.redirectErrorStream(true);
Process process = builder.start(); // 多线程处理加快速度-解决数据丢失
// 线程处理
threadSyn(process);
process.waitFor(); // 进程等待机制,必须要有,否则不生成mp4!!!
logger.info("生成mp4视频为:{}", mp4Path);
// 生成mp4视频保存路径
setVideoPath(mp4Path); logger.info("===============视频转码结束================="); return mp4Path;
}
else if(checkVideoSuffix(suffix)==200){
return oldfilepath; }
else{
return ("该文件暂不支持转换");
} } catch (Exception e) {
e.printStackTrace();
return mp4Path;
}
} /**
* 对ffmpeg无法解析的文件格式(wmv9,rm,rmvb等),
* 可以先用(mencoder)转换为avi(ffmpeg能解析的)格式.再用ffmpeg解析为指定格式
* @param oldfilepath 临时源视频文件路径
* @return
*/
private String processAVI(String oldfilepath) {
logger.info("调用了mencoder.exe工具");
String tempPath = TEMPORARY_VIDEO_PATH + Long.toString(new Date().getTime());
List<String> commend = new ArrayList<String>();
commend.add(MENCODER_PATH); // 指定mencoder.exe工具的位置
commend.add(oldfilepath); // 指定源视频的位置
commend.add("-oac");
commend.add("mp3lame"); // lavc 原mp3lame
commend.add("-lameopts");
commend.add("preset=64");
commend.add("-ovc");
commend.add("xvid"); // mpg4(xvid),AVC(h.264/x264),只有h264才是公认的MP4标准编码,如果ck播放不了,就来调整这里
commend.add("-xvidencopts"); // xvidencopts或x264encopts
commend.add("bitrate=600"); // 600或440
commend.add("-of");
commend.add("avi");
commend.add("-o");
commend.add(tempPath + ".avi"); // 存放路径+名称,生成.avi视频 // 打印出转换命令
StringBuffer test = new StringBuffer();
for (int i = 0; i < commend.size(); i++) {
test.append(commend.get(i) + " ");
}
logger.info("mencoder输入的命令:" + test);
try {
// 调用线程命令启动转码
ProcessBuilder builder = new ProcessBuilder();
builder.command(commend);
Process process = builder.start(); // 多线程处理加快速度-解决数据丢失
// 线程处理
threadSyn(process);
// 等Mencoder进程转换结束,再调用ffmepg进程非常重要!!!
process.waitFor();
logger.info("Mencoder进程结束");
return tempPath + ".avi"; // 返回转为AVI以后的视频地址 } catch (Exception e) {
e.printStackTrace();
return null;
}
} /**
* 视频截图功能
* @param sourceVideoPath 需要被截图的视频路径(包含文件名和后缀名)
* @return
*/
public boolean processImg(String sourceVideoPath) {
String imageFile = "images/videoFile/";
String fileImpPath = ResourceService.rb.getString("filePath");
// 先确保保存截图的文件夹存在
File tempFile = new File(fileImpPath + imageFile);
if (tempFile.exists()) {
if (tempFile.isDirectory()) {
logger.info("该截图保存文件夹存在。");
} else {
logger.info("同名的截图保存文件存在,不能创建文件夹。");
}
} else {
logger.info("截图保存文件夹不存在,创建该文件夹。");
tempFile.mkdir();
} File file = new File(sourceVideoPath);
String fileName = file.getName(); // 获取视频文件的名称。
String fileRealName = fileName.substring(0, fileName.lastIndexOf(".")); // 获取不加后缀名的视频名
imageFile = imageFile + fileRealName + ".jpg";
List<String> commend = new ArrayList<String>();
// 第一帧: 00:00:01
// 截图命令:time ffmpeg -ss 00:00:01 -i test1.flv -f image2 -y test1.jpg
commend.add(FFMPEG_PATH); // 指定ffmpeg工具的路径
commend.add("-ss");
commend.add("00:00:03"); // 3是代表第3秒的时候截图
commend.add("-i");
commend.add(sourceVideoPath); // 截图的视频路径
commend.add("-f");
commend.add("image2");
commend.add("-y");
commend.add(fileImpPath + imageFile); // 生成截图xxx.jpg // 打印截图命令
StringBuffer test = new StringBuffer();
for (int i = 0; i < commend.size(); i++) {
test.append(commend.get(i) + " ");
}
logger.info("截图命令:" + test); // 转码后完成截图功能-还是得用线程来解决
try {
// 调用线程处理命令
ProcessBuilder builder = new ProcessBuilder();
builder.command(commend);
Process process = builder.start();
// 线程处理
threadSyn(process);
// 等Mencoder进程转换结束,再调用ffmepg进程非常重要!!!
process.waitFor();
setVideoImg(imageFile);
// 视频大小
String videoSize = FileUploadToolUtil.getSize(file);
setSize(videoSize);
logger.info("截图进程结束,视频大小:{}", getSize());
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
} /**
* 多线程处理
* @param process
*/
public void threadSyn(Process process) {
// 获取进程的标准输入流
final InputStream is1 = process.getInputStream();
// 获取进程的错误流
final InputStream is2 = process.getErrorStream();
// 启动两个线程,一个线程负责读标准输出流,另一个负责读标准错误流
new Thread() {
public void run() {
BufferedReader br = new BufferedReader(new InputStreamReader(is1));
try {
String lineB = null;
while ((lineB = br.readLine()) != null) {
if (lineB != null) {
logger.info(lineB); //必须取走线程信息避免堵塞
}
}
} catch (IOException e) {
e.printStackTrace();
}
// 关闭流
finally {
try {
is1.close();
} catch (IOException e) {
e.printStackTrace();
}
} }
}.start();
new Thread() {
public void run() {
BufferedReader br2 = new BufferedReader(new InputStreamReader(is2));
try {
String lineC = null;
while ((lineC = br2.readLine()) != null) {
if (lineC != null) {
logger.info(lineC); //必须取走线程信息避免堵塞
}
}
} catch (IOException e) {
e.printStackTrace();
} // 关闭流
finally {
try {
is2.close();
} catch (IOException e) {
e.printStackTrace();
}
} }
}.start();
} /**
* 检查文件类型,检查非MP4后缀
* asx,asf,mpg,wmv,3gp,mp4,mov,avi,flv等使用ffmpeg能解析
* wmv9,rm,rmvb等ffmpeg无法解析,先用别的工具(mencoder)转换为avi(ffmpeg能解析的)格式
* @param suffixName 后缀名
* @return
*/
public int checkVideoSuffix(String suffixName) {
if (suffixName.equals("avi")) {
return 0;
} else if (suffixName.equals("mpg")) {
return 0;
} else if (suffixName.equals("wmv")) {
return 0;
} else if (suffixName.equals("3gp")) {
return 0;
} else if (suffixName.equals("mov")) {
return 0;
} else if (suffixName.equals("asf")) {
return 0;
} else if (suffixName.equals("asx")) {
return 0;
} else if (suffixName.equals("flv")) {
return 0;
} else if (suffixName.equals("mkv")) {
return 0;
} else if (suffixName.equals("wmv9")) {
return 1;
} else if (suffixName.equals("rm")) {
return 1;
} else if (suffixName.equals("rmvb")) {
return 1;
} else if (suffixName.equals("mp4")) { // MP4不转码
return 200;
}
return 9;
} /**
* 视频写入本地磁盘/服务器
* @param file 上传文件
* @param filePath 存储位置
* @param fileName 文件名称
* @return
*/
public boolean uploadVideo(MultipartFile file, String filePath, String fileName) {
// 上传到本地磁盘/服务器
try {
logger.info("上传的视频写入本地磁盘/服务器");
InputStream is = file.getInputStream();
OutputStream os = new FileOutputStream(new File(filePath, fileName));
int len = 0;
byte[] buffer = new byte[2048]; while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
os.close();
os.flush();
is.close();
return true;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
// public void run() {
// try {
// // 转换并截图
// String filePath = "D:\\video\\old\\test.avi";
// DisposeVideoUtils cv = new DisposeVideoUtils(filePath);
// cv.beginConver();
//
// // 仅截图
// // ProcessFlvImg pfi = new ProcessFlvImg();
// // pfi.processImg("D:\\video\\old\\test.avi");
//
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
private boolean processFLV(String oldfilepath) { if (!checkfile(PATH)) {
System.out.println(oldfilepath + " is not file");
return false;
} List commend = new java.util.ArrayList();
commend.add(FFMPEG_PATH);
commend.add("-i");
commend.add(oldfilepath);
commend.add("-vcodec");
commend.add("libx264");
commend.add("-acodec");
commend.add("aac");
commend.add("-s");
commend.add("1280x720");
commend.add("-vprofile");
commend.add("high");
// commend.add("-vlevel");
// commend.add("3.1");
// commend.add("-coder");
// commend.add("1");
// -vcodec libx264 -acodec aac -ab 128k -ar 16k -ac 2 -f mp4 -y
// commend.add("-movflags");
// commend.add("faststart");
// commend.add("-force_key_frames");
// commend.add("1");
// commend.add("-strict");
// commend.add("experimental");
commend.add("-r");
commend.add("25");
commend.add("-g");
commend.add("30");
commend.add("-bf");
commend.add("2");
commend.add("-ab");
commend.add("128k");
commend.add("-ar");
commend.add("44100");
commend.add("-ac");
commend.add("2");
// commend.add("-b:v");
// commend.add("1.6M");
// commend.add("-sc_threshold");
// commend.add("0");
commend.add("-f"); // 添加参数"-y",该参数指定将覆盖已存在的文件
commend.add("mp4"); // 添加参数"-y",该参数指定将覆盖已存在的文件
commend.add("-y");
commend.add(uploadFolder + filerealname + ".mp4");
try {
ProcessBuilder builder = new ProcessBuilder();
String cmd = commend.toString();
builder.command(commend);
Process p = builder.start();
doWaitFor(p);
p.destroy();
deleteFile(oldfilepath);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public void deleteFile(String filepath) {
File file = new File(filepath);
if (PATH.equals(filepath)) {
if (file.delete()) {
System.out.println("文件" + filepath + "已删除");
}
} else {
if (file.delete()) {
System.out.println("文件" + filepath + "已删除 ");
}
File filedelete2 = new File(PATH);
if (filedelete2.delete()) {
System.out.println("文件" + PATH + "已删除");
}
}
}
private boolean checkfile(String path) {
File file = new File(path);
if (!file.isFile()) {
return false;
} else {
return true;
}
}
public int doWaitFor(Process p) {
InputStream in = null;
InputStream err = null;
int exitValue = -1; // returned to caller when p is finished
try {
System.out.println("comeing");
in = p.getInputStream();
err = p.getErrorStream();
boolean finished = false; // Set to true when p is finished while (!finished) {
try {
while (in.available() > 0) {
Character c = new Character((char) in.read());
System.out.print(c);
}
while (err.available() > 0) {
Character c = new Character((char) err.read());
System.out.print(c);
} exitValue = p.exitValue();
finished = true; } catch (IllegalThreadStateException e) {
Thread.currentThread().sleep(500);
}
}
} catch (Exception e) {
System.err.println("doWaitFor();: unexpected exception - "
+ e.getMessage());
} finally {
try {
if (in != null) {
in.close();
} } catch (IOException e) {
System.out.println(e.getMessage());
}
if (err != null) {
try {
err.close();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
return exitValue;
} }
2. 将视频文件转码保存在本地之后,因为没有前端操作,一切都是在后台默默的进行,所以我们要读取该文件(保存在了本地磁盘),这必将是通过io流读取文件并获得,但是由于minio的上传方法要求是Multipart文件,所以将io流转换为Multipart文件就可以了。
/**
*
* @Description io流转换为MultipartFile---------返回MultipartFile文件
* @return org.springframework.web.multipart.MultipartFile
* @date 2020/12/30
* @auther liuminghui
*/
public static MultipartFile getFile( String filePath) throws IOException { File file = new File(filePath);
FileItem fileItem = new DiskFileItem("copyfile.txt", Files.probeContentType(file.toPath()),false,file.getName(),(int)file.length(),file.getParentFile());
byte[] buffer = new byte[4096];
int n;
try (InputStream inputStream = new FileInputStream(file); OutputStream os = fileItem.getOutputStream()){
while ( (n = inputStream.read(buffer,0,4096)) != -1){
os.write(buffer,0,n);
}
//也可以用IOUtils.copy(inputStream,os);
MultipartFile multipartFile = new CommonsMultipartFile(fileItem);
System.out.println(multipartFile.getName());
return multipartFile;
}catch (IOException e){
e.printStackTrace();
}
return null;
}
3, 大致就是这样,还有很多文件的参数的方法都可以在各大网站上搜索到的,这里纯属是记录一下工作中遇到的各种问题和最终解决,感谢平台!
io流转换为Multipart文件的更多相关文章
- Java 基础(四)| IO 流之使用文件流的正确姿势
为跳槽面试做准备,今天开始进入 Java 基础的复习.希望基础不好的同学看完这篇文章,能掌握泛型,而基础好的同学权当复习,希望看完这篇文章能够起一点你的青涩记忆. 一.什么是 IO 流? 想象一个场景 ...
- IO流-批量修改文件名称案例
/* * 源文件名: 桌面-我们今天学习IO流了哈哈哈哈-001.jpg * 修改后文件名: 桌面-000x.jpg */public class File_listFiles_upda ...
- Java IO流之普通文件流和随机读写流区别
普通文件流和随机读写流区别 普通文件流:http://blog.csdn.net/baidu_37107022/article/details/71056011 FileInputStream和Fil ...
- IO流-复制多极文件夹(递归实现)
package com.io.test; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import ...
- java Io流输出指定文件的内容
package com.hp.io; import java.io.*; public class BufferedReaderTest{ /** *@param 想想 */ public st ...
- IO流(五)__文件的递归、Properties、打印流PrintStream与PrintWriter、序列流SequenceInputStream
一.文件的遍历 1.需求:对指定目录进行所有的内容的列出(包含子目录的内容)-文件的深度遍历 思想:递归的思想,在递归的时候要记住递归的层次. public class FileTest { publ ...
- C#常用IO流与读写文件
.文件系统 ()文件系统类的介绍 文件操作类大都在System.IO命名空间里.FileSystemInfo类是任何文件系统类的基类:FileInfo与File表示文件系统中的文件:Directory ...
- Java笔记(二十七)……IO流中 File文件对象与Properties类
File类 用来将文件或目录封装成对象 方便对文件或目录信息进行处理 File对象可以作为参数传递给流进行操作 File类常用方法 创建 booleancreateNewFile():创建新文件,如果 ...
- JAVA IO流编程 实现文件的写入、写出以及拷贝
一.流的概念 流:数据在数据源(文件)和程序(内存)之间经历的路径. 输入流:数据从数据源(文件)到程序(内存)的路径. 输出流:数据从程序(内存)到数据源(文件)的路径. 以内存为参照,如果数据向内 ...
- C#常用IO流与读写文件 (转)
源自https://www.cnblogs.com/liyangLife/p/4797583.html 谢谢 1.文件系统 (1)文件系统类的介绍 文件操作类大都在System.IO命名空间里.Fil ...
随机推荐
- 【学习笔记】动态树 Link-Cut Tree
- 闲话 LCT 优秀博客: \(\color{black}{\textsf{F}}\color{red}{\textsf{lashHu}}\) 大佬的 cnblogs:https://www.cnb ...
- 9月23日内容总结——pycharm的安装与使用、python语法规范与注释、变量和常量、索引取值以及部分数据类型简介
今日内容总结 目录 今日内容总结 一.pycharm的安装 1.软件介绍 2.正版安装 1.下载软件 2.安装软件 3.其他方法安装(需要先下载相关资源) ①无限试用法 ②傻瓜式激活法 ③淘宝购买 二 ...
- SAOI 题解汇总
题解汇总 A. Chery 的魔法药水与 lrc 的韭菜 所有部分分代码及标程均在这里. 这个题目是我们前面的月考卷子改编后的 idea,去年就出了,今年翻出来经过加强得到了这道入门 题目. 首先,不 ...
- Django框架之drf:9、接口文档,coreapi的使用,JWT原理、介绍、快速使用、定制、认证
目录 Django框架之drf 一.接口文档 二.CoreAPI文档生成器 1.使用方法 三.JWT 1.JWT原理及介绍 2.JWP快速使用 3.定制返回格式 4.JTW的认证类 Django框架之 ...
- [USACO17JAN]Cow Dance Show S更新ing
这道题目是二分舞台大小,为什么能用二分呢?因为如果mid成立 则mid~r都成立,如果mid不成立l~mid就都不成立,也就是严格单调,所以可以使用二分快速找到k. check函数的思路: 实现:在舞 ...
- 主题样式选择效果代码及css样式
先上效果图: 主要页面代码及样式: <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> ...
- eigen的简单用法汇总
Eigen帮助文档的地址:http://eigen.tuxfamily.org/dox/pages.html Eigen的论坛:http://forum.kde.org/viewforum.php?f ...
- 学习Java Day15
今天学习了自定义类
- 最大K段和
题目大意 有一个长度为 \(N\) 的序列 \(A\) .他希望从中选出不超过 \(K\) 个连续子段,满足它们两两不相交,求总和的最大值(可以一段也不选,答案为 \(0\)). 分析 很容易想到 \ ...
- 梅毒感染者能否应用TNF抑制剂
对于伴发的未经控制的任何严重感染,都不适合使用TNF抑制剂.在1998年国际上首个TNF抑制剂获批治疗类风湿关节炎(RA)以来,这就是广大临床医生和风湿性疾病患者的共识.在临床实践中,需要权衡药物的利 ...