本地文件和网络文件读取
一. SpringBoot的RestTemplate配置
二. 文件读取
java
复制代码
package com.zzc.component.download;
import com.zzc.common.utils.StrUtils;
import lombok.Data;
import javax.servlet.http.HttpServletResponse;
public class RangeEntity {
private String range;
private long contentLength;
private String contentRange;
private Long firstPos;
private Long lastPos;
public RangeEntity(String range, long contentLength) {
parseRange(range, contentLength);
}
private void parseRange(String range, long contentLength) {
this.range = range;
this.contentLength = contentLength;
if (StrUtils.isBlank(range) || "null".equalsIgnoreCase(range)) {
this.firstPos = 0L;
this.contentLength = contentLength;
this.contentRange = "bytes " + 0 + "-" + (contentLength - 1) + "/" + contentLength;
return;
}
range = range.trim();
String rangeBytes = range.replaceAll("bytes=", "").replaceAll(" ", "");
if (rangeBytes.endsWith("-")) {//bytes=99999-
rangeBytes = rangeBytes.substring(0, rangeBytes.indexOf("-"));
this.firstPos = Long.valueOf(rangeBytes.trim());
this.contentLength = contentLength - this.firstPos;
this.contentRange = "bytes " + this.firstPos + "-" + (contentLength - 1) + "/" + contentLength;
} else {
this.firstPos = Long.valueOf(rangeBytes.substring(0, rangeBytes.indexOf("-")));
this.lastPos = Long.valueOf(rangeBytes.substring(rangeBytes.indexOf("-") + 1));
this.contentLength = this.lastPos - this.firstPos + 1;
this.contentRange = "bytes " + this.firstPos + "-" + this.lastPos + "/" + contentLength;
}
}
public int getStatusCode(boolean isIeOrPlayer) {
if (((StrUtils.isBlank(range) || "null".equalsIgnoreCase(range)) && isIeOrPlayer)
|| (this.firstPos == null && this.lastPos == null)) {
return HttpServletResponse.SC_OK;
} else {
return HttpServletResponse.SC_PARTIAL_CONTENT;
}
}
public String getRange() {
return range;
}
public long getContentLength() {
return contentLength;
}
public String getContentRange() {
return contentRange;
}
public boolean isNotRange() {
return lastPos == null || firstPos == 0;
}
public Long getFirstPos() {
return firstPos;
}
}
java
复制代码
package com.zzc.component.download;
import com.zzc.common.utils.StrUtils;
import javax.servlet.http.HttpServletRequest;
public class HeaderUtils {
public static boolean isIeBrowser(HttpServletRequest request) {
String[] isSignals = {"MSIE", "Trident", "Edge", "Safari"};
String userAgent = request.getHeader("User-Agent");
if (StrUtils.isNotBlank(userAgent)) {
for (String signal : isSignals) {
if (userAgent.contains(signal)) {
return true;
}
}
}
return false;
}
public static boolean isWinPlayer(HttpServletRequest request) {
String userAgent = request.getHeader("User-Agent");
if (StrUtils.isNotBlank(userAgent)) {
return userAgent.contains("Windows-Media-Player");
}
return false;
}
}
- HeaderConstant请求header对应字段
java
复制代码
package com.zzc.component.download;
public class HeaderConstant {
public static final String KEY_ACCEPT_RANGE = "Accept-Ranges";
public static final String KEY_CONTENT_LENGTH = "Content-Length";
public static final String KEY_CONTENT_RANGE = "Content-Range";
public static final String KEY_CONTENT_TYPE = "Content-Type";
public static final String KEY_CONTENT_DISPOSITION = "Content-Disposition";
}
java
复制代码
package com.zzc.component.download;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface FileComponent {
void play(String path, HttpServletRequest request, HttpServletResponse response);
void download(String path, HttpServletRequest request, HttpServletResponse response);
}
java
复制代码
package com.zzc.component.download;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;
@Slf4j
@Component
public class FileComponentImpl implements FileComponent {
@Override
public void play(String path, HttpServletRequest request, HttpServletResponse response) {
RandomAccessFile raf = null;
try {
File file = new File(path);
if (!file.exists()) {
throw new RuntimeException();
}
long length = file.length();
String range = request.getHeader("Range");
String filename = file.getName();
String contentType = "video/mp4";//文件类型
RangeEntity rangeEntity = new RangeEntity(range, length);
addHeader(request, response, rangeEntity, contentType, filename);
raf = new RandomAccessFile(file, "r");
OutputStream outputStream = response.getOutputStream();
if (rangeEntity.isNotRange()) {
channelCopy(raf.getChannel(), Channels.newChannel(outputStream), rangeEntity.getFirstPos(), length);
outputStream.flush();
return;
}
long contentLength = rangeEntity.getContentLength();
channelCopy(raf.getChannel(), Channels.newChannel(outputStream), rangeEntity.getFirstPos(), contentLength);
outputStream.flush();
//outputStream.close();
} catch (Exception e) {
} finally {
if (raf != null) {
try {
raf.close();
} catch (IOException e) {
}
}
}
}
private static void addHeader(HttpServletRequest request, HttpServletResponse response, RangeEntity rangeEntity, String contentType, String filename) {
boolean isIeOrPlayer = HeaderUtils.isWinPlayer(request) || HeaderUtils.isIeBrowser(request);
response.setStatus(rangeEntity.getStatusCode(isIeOrPlayer));
response.setHeader(HeaderConstant.KEY_ACCEPT_RANGE, "bytes");
response.setHeader(HeaderConstant.KEY_CONTENT_LENGTH, rangeEntity.getContentLength() + "");
if (rangeEntity.getContentRange() != null) {
response.setHeader(HeaderConstant.KEY_CONTENT_RANGE, rangeEntity.getContentRange());
}
response.setHeader(HeaderConstant.KEY_CONTENT_TYPE, contentType);
//response.setHeader(HeaderConstant.KEY_CONTENT_DISPOSITION, "inline; filename=\"" + filename + "\"; filename*=utf-8''" + filename);
if (isIeOrPlayer) {
try {
response.setHeader(HeaderConstant.KEY_CONTENT_DISPOSITION, "inline; filename=\"" + URLEncoder.encode(filename, "UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
}
@Override
public void download(String path, HttpServletRequest request, HttpServletResponse response) {
FileInputStream is = null;
try {
File file = new File(path);
if (!file.exists()) {
throw new RuntimeException();
}
String name = file.getName();
long length = file.length();
String contentType = "video/mp4";
is = new FileInputStream(file);
response.setHeader(HeaderConstant.KEY_CONTENT_LENGTH, length + "");
response.setHeader(HeaderConstant.KEY_CONTENT_TYPE, contentType);
response.setHeader(HeaderConstant.KEY_CONTENT_DISPOSITION, "attachment;filename=" + name);
ServletOutputStream outputStream = response.getOutputStream();
channelCopy(is.getChannel(), Channels.newChannel(outputStream), 0, length);
outputStream.flush();
outputStream.close();
} catch (Exception e) {
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
}
}
}
}
private static void channelCopy(FileChannel readableChannel, WritableByteChannel writableByteChannel, long position, long size) {
try {
if (writableByteChannel.isOpen()) {
readableChannel.transferTo(position, size, writableByteChannel);
}
} catch (Exception e) {
} finally {
try {
readableChannel.close();
writableByteChannel.close();
} catch (IOException e) {
}
}
}
}
java
复制代码
package com.zzc.component.download;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface OBSComponent {
void download(String url, HttpServletRequest request, HttpServletResponse response);
}
java
复制代码
package com.zzc.component.download;
import com.zzc.component.http.RestTemplateComponent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.client.ResponseExtractor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Component
public class OBSComponentImpl implements OBSComponent {
@Autowired
private RestTemplateComponent restTemplateComponent;
@Override
public void download(String url, HttpServletRequest request, HttpServletResponse response) {
Map<String, String> headers = new HashMap<>();
ResponseExtractor<Boolean> extractor = new ResponseExtractor<Boolean>() {
@Override
public Boolean extractData(ClientHttpResponse clientHttpResponse) throws IOException {
HttpStatus statusCode = clientHttpResponse.getStatusCode();
if (statusCode.isError()) {
return false;
}
//TODO head
Map<String, String> header = new HashMap<>();
return true;
}
};
restTemplateComponent.transfer(url, headers, extractor);
}
private static void channelCopy(ReadableByteChannel readableByteChannel, WritableByteChannel writableByteChannel) {
try {
ByteBuffer buffer = ByteBuffer.allocateDirect(32 * 1024);
while (writableByteChannel.isOpen() && readableByteChannel.read(buffer) != -1) {
buffer.flip();
writableByteChannel.write(buffer);
buffer.compact();
}
buffer.flip();
while (writableByteChannel.isOpen() && buffer.hasRemaining()) {
writableByteChannel.write(buffer);
}
} catch (Exception e) {
} finally {
try {
readableByteChannel.close();
writableByteChannel.close();
} catch (IOException e) {
}
}
}
}