为了防止直接请求文件而导致数据被采集,通过接口逻辑判断后再输出文件流的方式模拟完成直接请求文件的操作,支持大文件流操作

JAVA代码:

package lan.dk.podcastserver.utils.multipart;

import lan.dk.podcastserver.utils.MimeTypeUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.FileTime;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; /**
* Created by kevin on 10/02/15.
*/
public class MultipartFileSender { protected final Logger logger = LoggerFactory.getLogger(this.getClass()); private static final int DEFAULT_BUFFER_SIZE = 20480; // ..bytes = 20KB.
private static final long DEFAULT_EXPIRE_TIME = 604800000L; // ..ms = 1 week.
private static final String MULTIPART_BOUNDARY = "MULTIPART_BYTERANGES"; Path filepath;
HttpServletRequest request;
HttpServletResponse response; public MultipartFileSender() {
} public static MultipartFileSender fromPath(Path path) {
return new MultipartFileSender().setFilepath(path);
} public static MultipartFileSender fromFile(File file) {
return new MultipartFileSender().setFilepath(file.toPath());
} public static MultipartFileSender fromURIString(String uri) {
return new MultipartFileSender().setFilepath(Paths.get(uri));
} //** internal setter **//
private MultipartFileSender setFilepath(Path filepath) {
this.filepath = filepath;
return this;
} public MultipartFileSender with(HttpServletRequest httpRequest) {
request = httpRequest;
return this;
} public MultipartFileSender with(HttpServletResponse httpResponse) {
response = httpResponse;
return this;
} public void serveResource() throws Exception {
if (response == null || request == null) {
return;
} if (!Files.exists(filepath)) {
logger.error("File doesn't exist at URI : {}", filepath.toAbsolutePath().toString());
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
} Long length = Files.size(filepath);
String fileName = filepath.getFileName().toString();
FileTime lastModifiedObj = Files.getLastModifiedTime(filepath); if (StringUtils.isEmpty(fileName) || lastModifiedObj == null) {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return;
}
long lastModified = LocalDateTime.ofInstant(lastModifiedObj.toInstant(), ZoneId.of(ZoneOffset.systemDefault().getId())).toEpochSecond(ZoneOffset.UTC);
String contentType = MimeTypeUtils.probeContentType(filepath); // Validate request headers for caching --------------------------------------------------- // If-None-Match header should contain "*" or ETag. If so, then return 304.
String ifNoneMatch = request.getHeader("If-None-Match");
if (ifNoneMatch != null && HttpUtils.matches(ifNoneMatch, fileName)) {
response.setHeader("ETag", fileName); // Required in 304.
response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
return;
} // If-Modified-Since header should be greater than LastModified. If so, then return 304.
// This header is ignored if any If-None-Match header is specified.
long ifModifiedSince = request.getDateHeader("If-Modified-Since");
if (ifNoneMatch == null && ifModifiedSince != -1 && ifModifiedSince + 1000 > lastModified) {
response.setHeader("ETag", fileName); // Required in 304.
response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
return;
} // Validate request headers for resume ---------------------------------------------------- // If-Match header should contain "*" or ETag. If not, then return 412.
String ifMatch = request.getHeader("If-Match");
if (ifMatch != null && !HttpUtils.matches(ifMatch, fileName)) {
response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
return;
} // If-Unmodified-Since header should be greater than LastModified. If not, then return 412.
long ifUnmodifiedSince = request.getDateHeader("If-Unmodified-Since");
if (ifUnmodifiedSince != -1 && ifUnmodifiedSince + 1000 <= lastModified) {
response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
return;
} // Validate and process range ------------------------------------------------------------- // Prepare some variables. The full Range represents the complete file.
Range full = new Range(0, length - 1, length);
List<Range> ranges = new ArrayList<>(); // Validate and process Range and If-Range headers.
String range = request.getHeader("Range");
if (range != null) { // Range header should match format "bytes=n-n,n-n,n-n...". If not, then return 416.
if (!range.matches("^bytes=\\d*-\\d*(,\\d*-\\d*)*$")) {
response.setHeader("Content-Range", "bytes */" + length); // Required in 416.
response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
return;
} String ifRange = request.getHeader("If-Range");
if (ifRange != null && !ifRange.equals(fileName)) {
try {
long ifRangeTime = request.getDateHeader("If-Range"); // Throws IAE if invalid.
if (ifRangeTime != -1) {
ranges.add(full);
}
} catch (IllegalArgumentException ignore) {
ranges.add(full);
}
} // If any valid If-Range header, then process each part of byte range.
if (ranges.isEmpty()) {
for (String part : range.substring(6).split(",")) {
// Assuming a file with length of 100, the following examples returns bytes at:
// 50-80 (50 to 80), 40- (40 to length=100), -20 (length-20=80 to length=100).
long start = Range.sublong(part, 0, part.indexOf("-"));
long end = Range.sublong(part, part.indexOf("-") + 1, part.length()); if (start == -1) {
start = length - end;
end = length - 1;
} else if (end == -1 || end > length - 1) {
end = length - 1;
} // Check if Range is syntactically valid. If not, then return 416.
if (start > end) {
response.setHeader("Content-Range", "bytes */" + length); // Required in 416.
response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
return;
} // Add range.
ranges.add(new Range(start, end, length));
}
}
} // Prepare and initialize response -------------------------------------------------------- // Get content type by file name and set content disposition.
String disposition = "inline"; // If content type is unknown, then set the default value.
// For all content types, see: http://www.w3schools.com/media/media_mimeref.asp
// To add new content types, add new mime-mapping entry in web.xml.
if (contentType == null) {
contentType = "application/octet-stream";
} else if (!contentType.startsWith("image")) {
// Else, expect for images, determine content disposition. If content type is supported by
// the browser, then set to inline, else attachment which will pop a 'save as' dialogue.
String accept = request.getHeader("Accept");
disposition = accept != null && HttpUtils.accepts(accept, contentType) ? "inline" : "attachment";
}
logger.debug("Content-Type : {}", contentType);
// Initialize response.
response.reset();
response.setBufferSize(DEFAULT_BUFFER_SIZE);
response.setHeader("Content-Type", contentType);
response.setHeader("Content-Disposition", disposition + ";filename=\"" + fileName + "\"");
logger.debug("Content-Disposition : {}", disposition);
response.setHeader("Accept-Ranges", "bytes");
response.setHeader("ETag", fileName);
response.setDateHeader("Last-Modified", lastModified);
response.setDateHeader("Expires", System.currentTimeMillis() + DEFAULT_EXPIRE_TIME); // Send requested file (part(s)) to client ------------------------------------------------ // Prepare streams.
try (InputStream input = new BufferedInputStream(Files.newInputStream(filepath));
OutputStream output = response.getOutputStream()) { if (ranges.isEmpty() || ranges.get(0) == full) { // Return full file.
logger.info("Return full file");
response.setContentType(contentType);
response.setHeader("Content-Range", "bytes " + full.start + "-" + full.end + "/" + full.total);
response.setHeader("Content-Length", String.valueOf(full.length));
Range.copy(input, output, length, full.start, full.length); } else if (ranges.size() == 1) { // Return single part of file.
Range r = ranges.get(0);
logger.info("Return 1 part of file : from ({}) to ({})", r.start, r.end);
response.setContentType(contentType);
response.setHeader("Content-Range", "bytes " + r.start + "-" + r.end + "/" + r.total);
response.setHeader("Content-Length", String.valueOf(r.length));
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // 206. // Copy single part range.
Range.copy(input, output, length, r.start, r.length); } else { // Return multiple parts of file.
response.setContentType("multipart/byteranges; boundary=" + MULTIPART_BOUNDARY);
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // 206. // Cast back to ServletOutputStream to get the easy println methods.
ServletOutputStream sos = (ServletOutputStream) output; // Copy multi part range.
for (Range r : ranges) {
logger.info("Return multi part of file : from ({}) to ({})", r.start, r.end);
// Add multipart boundary and header fields for every range.
sos.println();
sos.println("--" + MULTIPART_BOUNDARY);
sos.println("Content-Type: " + contentType);
sos.println("Content-Range: bytes " + r.start + "-" + r.end + "/" + r.total); // Copy single part range of multi part range.
Range.copy(input, output, length, r.start, r.length);
} // End with multipart boundary.
sos.println();
sos.println("--" + MULTIPART_BOUNDARY + "--");
}
} } private static class Range {
long start;
long end;
long length;
long total; /**
* Construct a byte range.
* @param start Start of the byte range.
* @param end End of the byte range.
* @param total Total length of the byte source.
*/
public Range(long start, long end, long total) {
this.start = start;
this.end = end;
this.length = end - start + 1;
this.total = total;
} public static long sublong(String value, int beginIndex, int endIndex) {
String substring = value.substring(beginIndex, endIndex);
return (substring.length() > 0) ? Long.parseLong(substring) : -1;
} private static void copy(InputStream input, OutputStream output, long inputSize, long start, long length) throws IOException {
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
int read; if (inputSize == length) {
// Write full range.
while ((read = input.read(buffer)) > 0) {
output.write(buffer, 0, read);
output.flush();
}
} else {
input.skip(start);
long toRead = length; while ((read = input.read(buffer)) > 0) {
if ((toRead -= read) > 0) {
output.write(buffer, 0, read);
output.flush();
} else {
output.write(buffer, 0, (int) toRead + read);
output.flush();
break;
}
}
}
}
}
private static class HttpUtils { /**
* Returns true if the given accept header accepts the given value.
* @param acceptHeader The accept header.
* @param toAccept The value to be accepted.
* @return True if the given accept header accepts the given value.
*/
public static boolean accepts(String acceptHeader, String toAccept) {
String[] acceptValues = acceptHeader.split("\\s*(,|;)\\s*");
Arrays.sort(acceptValues); return Arrays.binarySearch(acceptValues, toAccept) > -1
|| Arrays.binarySearch(acceptValues, toAccept.replaceAll("/.*$", "/*")) > -1
|| Arrays.binarySearch(acceptValues, "*/*") > -1;
} /**
* Returns true if the given match header matches the given value.
* @param matchHeader The match header.
* @param toMatch The value to be matched.
* @return True if the given match header matches the given value.
*/
public static boolean matches(String matchHeader, String toMatch) {
String[] matchValues = matchHeader.split("\\s*,\\s*");
Arrays.sort(matchValues);
return Arrays.binarySearch(matchValues, toMatch) > -1
|| Arrays.binarySearch(matchValues, "*") > -1;
}
}
}

C#代码

    /// <summary>
/// 文件分段,多段输出
/// </summary>
public class MultipartFileSender
{
protected log4net.ILog logger = log4net.LogManager.GetLogger(typeof(MultipartFileSender));
private const int DEFAULT_BUFFER_SIZE = ; // ..bytes = 20KB.
private const string MULTIPART_BOUNDARY = "MULTIPART_BYTERANGES";
string filepath;
readonly HttpRequestBase request;
private HttpResponseBase response;
/// <summary>
/// 初始化
/// </summary>
/// <param name="path">文件物理路径</param>
public MultipartFileSender(string path, HttpRequestBase req, HttpResponseBase resp)
{
filepath = path;
request = req;
response = resp;
}
/// <summary>
/// 输出
/// </summary>
public void ServeResource()
{
if (response == null || request == null)
{
return;
}
if (filepath.IsNullOrWiteSpace() || !File.Exists(filepath))
{
logger.Error($"请求文件不存在 : {filepath}");
response.StatusCode = ;
response.Flush();
response.Close();
return;
}
FileInfo fileinfo = new FileInfo(filepath);
long length = fileinfo.Length;
string fileName = Path.GetFileName(filepath);
string etag = fileName.GetMd5_16();
DateTime lastModifiedTime = fileinfo.LastWriteTimeUtc;
long lastModified = (long)(lastModifiedTime - new DateTime(, , , , , , )).TotalSeconds;
string contentType = MimeMapping.GetMimeMapping(fileName); // Validate request headers for caching --------------------------------------------------- // If-None-Match header should contain "*" or ETag. If so, then return 304.
string ifNoneMatch = request.Headers["If-None-Match"];
if (ifNoneMatch != null && HttpMatches(ifNoneMatch, etag))
{
response.AddHeader("ETag", etag); // Required in 304.
response.StatusCode = ;
response.Flush();
response.Close(); return;
} // If-Modified-Since header should be greater than LastModified. If so, then return 304.
// This header is ignored if any If-None-Match header is specified.
long ifModifiedSince = request.Headers["If-Modified-Since"]?.ToLong() ?? -;
if (ifNoneMatch == null && ifModifiedSince != - && ifModifiedSince + > lastModified)
{
response.AddHeader("ETag", etag); // Required in 304.
response.StatusCode = ;
response.Flush();
response.Close();
return;
} // Validate request headers for resume ---------------------------------------------------- // If-Match header should contain "*" or ETag. If not, then return 412.
string ifMatch = request.Headers["If-Match"];
if (ifMatch != null && !HttpMatches(ifMatch, etag))
{
response.StatusCode = ;
response.Flush();
response.Close();
return;
} // If-Unmodified-Since header should be greater than LastModified. If not, then return 412.
long ifUnmodifiedSince = request.Headers["If-Unmodified-Since"]?.ToLong() ?? -;
if (ifUnmodifiedSince != - && ifUnmodifiedSince + <= lastModified)
{
response.StatusCode = ;
response.Flush();
response.Close();
return;
} // Validate and process range ------------------------------------------------------------- // Prepare some variables. The full Range represents the complete file.
HttpRange full = new HttpRange(, length - , length);
List<HttpRange> ranges = new List<HttpRange>(); // Validate and process Range and If-Range headers.
string range = request.Headers["Range"];
if (range != null)
{
// Range header should match format "bytes=n-n,n-n,n-n...". If not, then return 416.
if (!Regex.IsMatch(range, "^bytes=\\d*-\\d*(,\\d*-\\d*)*$"))
{
response.AddHeader("Content-Range", "bytes */" + length); // Required in 416.
response.StatusCode = ;
response.Flush();
response.Close();
return;
} string ifRange = request.Headers["If-Range"];
if (ifRange != null && ifRange != etag)
{
try
{
long ifRangeTime = ifRange.ToLong(); // Throws IAE if invalid.
if (ifRangeTime != -)
{
ranges.Add(full);
}
}
catch (Exception ignore)
{
ranges.Add(full);
}
} // If any valid If-Range header, then process each part of byte range.
if (ranges.Count == )
{
foreach (var part in range.Substring().Split(','))
{ // Assuming a file with length of 100, the following examples returns bytes at:
// 50-80 (50 to 80), 40- (40 to length=100), -20 (length-20=80 to length=100).
long start = HttpRange.Sublong(part, , part.IndexOf("-", StringComparison.Ordinal));
long end = HttpRange.Sublong(part, part.IndexOf("-", StringComparison.Ordinal) + , part.Length); if (start == -)
{
start = length - end;
end = length - ;
}
else if (end == - || end > length - )
{
end = length - ;
} // Check if Range is syntactically valid. If not, then return 416.
if (start > end)
{
response.AddHeader("Content-Range", "bytes */" + length); // Required in 416.
response.StatusCode = ;
response.Flush();
response.Close();
return;
}
// Add range.
ranges.Add(new HttpRange(start, end, length));
}
}
}
// Prepare and initialize response -------------------------------------------------------- // Get content type by file name and set content disposition.
string disposition = "inline"; // If content type is unknown, then set the default value.
// For all content types, see: http://www.w3schools.com/media/media_mimeref.asp
// To add new content types, add new mime-mapping entry in web.xml.
if (contentType == null)
{
contentType = "application/octet-stream";
}
else if (!contentType.StartsWith("image"))
{
// Else, expect for images, determine content disposition. If content type is supported by
// the browser, then set to inline, else attachment which will pop a 'save as' dialogue.
string accept = request.Headers["Accept"];
disposition = accept != null && HttpAccepts(accept, contentType) ? "inline" : "attachment";
} // Initialize response.
response.Clear();
response.ClearContent();
response.ClearHeaders(); response.Buffer = true;
response.AddHeader("Content-Type", contentType);
response.AddHeader("Content-Disposition", disposition + ";filename=\"" + fileName + "\""); response.AddHeader("Accept-Ranges", "bytes");
response.AddHeader("ETag", etag);
response.Cache.SetLastModified(lastModifiedTime);
response.Cache.SetExpires(DateTime.UtcNow.AddDays()); // Send requested file (part(s)) to client ------------------------------------------------ // Prepare streams.
using (FileStream input = new FileStream(filepath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (Stream output = response.OutputStream)
{ if (ranges.Count == || ranges[] == full)
{
if (logger.IsDebugEnabled)
logger.Info($"Return full file,{filepath}");
response.ContentType = contentType;
response.AddHeader("Content-Range", "bytes " + full.start + "-" + full.end + "/" + full.total);
response.AddHeader("Content-Length", full.length.ToString());
HttpRange.Copy(input, output, length, full.start, full.length); }
else if (ranges.Count == )
{ // Return single part of file.
HttpRange r = ranges[];
logger.Info($"Return 1 part of file :{fileName}, from ({r.start}) to ({r.end})");
response.ContentType=contentType;
response.AddHeader("Content-Range", "bytes " + r.start + "-" + r.end + "/" + r.total);
response.AddHeader("Content-Length", r.length.ToString());
response.StatusCode = ;
// Copy single part range.
HttpRange.Copy(input, output, length, r.start, r.length);
}
else
{ // Return multiple parts of file.
response.ContentType="multipart/byteranges; boundary=" + MULTIPART_BOUNDARY;
response.StatusCode = ;
// Cast back to ServletOutputStream to get the easy println methods. // Copy multi part range.
foreach (HttpRange r in ranges)
{
if (logger.IsDebugEnabled)
logger.Info($"Return multi part of file :{fileName} from ({r.start}) to ({r.end})");
// Copy single part range of multi part range.
HttpRange.Copy(input, output, length, r.start, r.length);
}
}
} }
/**
* Returns true if the given accept header accepts the given value.
* @param acceptHeader The accept header.
* @param toAccept The value to be accepted.
* @return True if the given accept header accepts the given value.
*/
public static bool HttpAccepts(string acceptHeader, string toAccept)
{
string[] acceptValues =
acceptHeader.Split(new char[] { ',', '|', ';' }, StringSplitOptions.RemoveEmptyEntries); return acceptValues.Contains(toAccept)
|| acceptValues.Contains(toAccept.Replace("/.*$", "/*"))
|| acceptValues.Contains("*/*");
} /**
* Returns true if the given match header matches the given value.
* @param matchHeader The match header.
* @param toMatch The value to be matched.
* @return True if the given match header matches the given value.
*/
public static bool HttpMatches(string matchHeader, string toMatch)
{
string[] matchValues = matchHeader.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
return matchValues.Contains(toMatch)
|| matchValues.Contains("*");
}
class HttpRange
{
public long start;
public long end;
public long length;
public long total; /**
* Construct a byte range.
* @param start Start of the byte range.
* @param end End of the byte range.
* @param total Total length of the byte source.
*/
public HttpRange(long start, long end, long total)
{
this.start = start;
this.end = end;
this.length = end - start + ;
this.total = total;
} public static long Sublong(string value, int beginIndex, int endIndex)
{
string substring = value.Substring(beginIndex, endIndex - beginIndex);
return (substring.Length > ) ? substring.ToLong() : -;
} public static void Copy(Stream input, Stream output, long inputSize, long start, long length)
{
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
int read; if (inputSize == length)
{ while ((read = input.Read(buffer, , buffer.Length)) > )
{
output.Write(buffer, , read);
output.Flush();
}
}
else
{
input.Seek(start, SeekOrigin.Begin);
long toRead = length; while ((read = input.Read(buffer, , buffer.Length)) > )
{
if ((toRead -= read) > )
{
output.Write(buffer, , read);
output.Flush();
}
else
{
output.Write(buffer, , (int)toRead + read);
output.Flush();
break;
}
}
}
}
}
}

数据流分段下载(Http之 Range)的更多相关文章

  1. NGINX(七)分段下载

    前言 nginx分段下载通过ngx_http_range_filter_module模块进行处理,关于HTTP分段下载过程,可以参考HTTP分段下载一文,主要分为一次请求一段和一次请求多段 涉及数据结 ...

  2. HTTP分段下载

    现代WEB服务器都支持大文件分段下载,加快下载速度,判断WEB服务器是否支持分段下载通过返回头是否有 Accept-Ranges: bytes 字段.分段下载分为两种,一种就是一次请求一个分段,一种就 ...

  3. 多线程分段下载研究的python实现(一)

    我一直对下载文件比较感兴趣.现在我下载文件大部分是用迅雷,但迅雷也有一些不如意的地方,内存占用大,一些不必要的功能太多,不可定制.尤其是最后一点.现在有些下载对useragent,cookie,aut ...

  4. .Net Core 实现 自定义Http的Range输出实现断点续传或者分段下载

    一.Http的Range请求头,结合相应头Accept-Ranges.Content-Range 可以实现如下功能: 1.断点续传.用于下载文件被中断后,继续下载. 2.大文件指定区块下载,如视频.音 ...

  5. iOS 简单的分段下载文件

    首先自己写个请求数据的类 首先.h文件 #import <Foundation/Foundation.h> @interface Downloaders : NSObject<NSU ...

  6. 前端js怎么实现大文件G级的断点续传(分块上传)和分段下载

    需求: 支持大文件批量上传(20G)和下载,同时需要保证上传期间用户电脑不出现卡死等体验: 内网百兆网络上传速度为12MB/S 服务器内存占用低 支持文件夹上传,文件夹中的文件数量达到1万个以上,且包 ...

  7. java http 分段下载

    http://www.iteye.com/topic/1136815 http://www.iteye.com/topic/1128336 http://blog.chinaunix.net/uid- ...

  8. iOS开发之文件(分段)下载

    1.HTTP HEAD方法 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:0 t ...

  9. IOS 上传下载

    下载地址:https://github.com/samsoffes/ssziparchive 注意:需要引入libz.dylib框架 // Unzipping NSString *zipPath = ...

随机推荐

  1. MVC模块化开发方案

    核心: 主要利用MVC的区域功能,实现项目模块独立开发和调试. 目标: 各个模块以独立MVC应用程序存在,即模块可独立开发和调试. 动态注册各个模块路由. 一:新建解决方案目录结构 如图: 二:Eas ...

  2. Web压力测试工具 LoadRunner12.x简易入门教程--(一)回放与录制

        LoadRunner12.x简易入门教程--(一)回放与录制 今天在这里分享一下LoadRunner12.x版本的入门使用方法,希望对刚接触LoadRunner的童鞋有所帮助. LoadRun ...

  3. bzoj千题计划305:bzoj2565: 最长双回文串(回文自动机)

    https://www.lydsy.com/JudgeOnline/problem.php?id=2565 正着构造回文自动机 倒过来再构造一个回文自动机 分别求出以位置i开始的和结尾的最长回文串 # ...

  4. 编写优秀jQuery插件技巧

    1. 把你的代码全部放在闭包里面 这是我用的最多的一条.但是有时候在闭包外面的方法会不能调用. 不过你的插件的代码只为你自己的插件服务,所以不存在这个问题,你可以把所有的代码都放在闭包里面. 而方法可 ...

  5. Tomcat环境变量,端口号,编码格式,项目路径,默认页的配置

    Tomcat的配置 1.配置环境变量 新建名为:CATALINA_HOME的系统变量,值为我们安装tomcat的目录 2端口号及编码的配置: 找到tomcat安装目录下的sonf下的server文件, ...

  6. CF101D Castle

    传送门 首先,一定要把所有点遍历一遍,这时答案应该是\(\frac{\sum 某个点第一次被遍历的时间点}{n-1}\quad\),而且每条边只能走两次,所以每次要遍历完某棵子树才能遍历其它子树. 考 ...

  7. H.264 SVC

    视频厂商POLYCOM,VIDYO和RADVISION等都推出H.264 SVC技术.针对H.264 SVC技术做个介绍. CISCO和POLYCOM都提供了免版税的H.264 SVC的版本. 其中o ...

  8. 批量下载Coursera及其他场景上的文件

    以下方法同样适用于其他场景的批量下载. 最近在学习Coursera退出的深度学习课程,我希望把课程提供的作业下载下来以备以后复习,但是课程有很多文件,比如说脸部识别一课中的参数就多达226个csv文件 ...

  9. python学习笔记:"爬虫+有道词典"实现一个简单的英译汉程序

    1.有道的翻译 网页:www.youdao.com Fig1 Fig2 Fig3 Fig4 再次点击"自动翻译"->选中'Network'->选中'第一项',如下: F ...

  10. 腾讯云YUM安装失效

    修改路由后,YUM安装失效,提示不能解析YUM源 yum clear chche yum makecache