diff --git a/src/main/java/com/github/tobato/fastdfs/domain/conn/DefaultConnection.java b/src/main/java/com/github/tobato/fastdfs/domain/conn/DefaultConnection.java index 7070cfd..7cacde8 100644 --- a/src/main/java/com/github/tobato/fastdfs/domain/conn/DefaultConnection.java +++ b/src/main/java/com/github/tobato/fastdfs/domain/conn/DefaultConnection.java @@ -2,7 +2,8 @@ import com.github.tobato.fastdfs.domain.proto.CmdConstants; import com.github.tobato.fastdfs.domain.proto.OtherConstants; -import com.github.tobato.fastdfs.domain.proto.mapper.BytesUtil; +import com.github.tobato.fastdfs.domain.proto.ProtoHead; +import com.github.tobato.fastdfs.domain.proto.StatusConstants; import com.github.tobato.fastdfs.exception.FdfsConnectException; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; @@ -14,7 +15,6 @@ import java.net.InetSocketAddress; import java.net.Socket; import java.nio.charset.Charset; -import java.util.Arrays; /** * 默认连接实现 @@ -60,16 +60,11 @@ public DefaultConnection(InetSocketAddress address, int soTimeout, int connectTi /** * 正常关闭连接 */ + @Override public synchronized void close() { LOGGER.debug("disconnect from {}", socket); - byte[] header = new byte[OtherConstants.FDFS_PROTO_PKG_LEN_SIZE + 2]; - Arrays.fill(header, (byte) 0); - - byte[] hex_len = BytesUtil.long2buff(0); - System.arraycopy(hex_len, 0, header, 0, hex_len.length); - header[OtherConstants.PROTO_HEADER_CMD_INDEX] = CmdConstants.FDFS_PROTO_CMD_QUIT; - header[OtherConstants.PROTO_HEADER_STATUS_INDEX] = (byte) 0; try { + byte[] header = new ProtoHead(CmdConstants.FDFS_PROTO_CMD_QUIT).toByte(); socket.getOutputStream().write(header); socket.close(); } catch (IOException e) { @@ -95,19 +90,13 @@ public boolean isClosed() { public boolean isValid() { LOGGER.debug("check connection status of {} ", this); try { - byte[] header = new byte[OtherConstants.FDFS_PROTO_PKG_LEN_SIZE + 2]; - Arrays.fill(header, (byte) 0); - - byte[] hex_len = BytesUtil.long2buff(0); - System.arraycopy(hex_len, 0, header, 0, hex_len.length); - header[OtherConstants.PROTO_HEADER_CMD_INDEX] = CmdConstants.FDFS_PROTO_CMD_ACTIVE_TEST; - header[OtherConstants.PROTO_HEADER_STATUS_INDEX] = (byte) 0; + byte[] header = new ProtoHead(CmdConstants.FDFS_PROTO_CMD_ACTIVE_TEST).toByte(); socket.getOutputStream().write(header); if (socket.getInputStream().read(header) != header.length) { return false; } - return header[OtherConstants.PROTO_HEADER_STATUS_INDEX] == 0 ? true : false; + return header[OtherConstants.PROTO_HEADER_STATUS_INDEX] == StatusConstants.FDFS_STORAGE_STATUS_INIT; } catch (IOException e) { LOGGER.error("valid connection error", e); return false; @@ -120,6 +109,7 @@ public boolean isValid() { * @return * @throws IOException */ + @Override public OutputStream getOutputStream() throws IOException { return socket.getOutputStream(); } @@ -130,6 +120,7 @@ public OutputStream getOutputStream() throws IOException { * @return * @throws IOException */ + @Override public InputStream getInputStream() throws IOException { return socket.getInputStream(); } @@ -139,6 +130,7 @@ public InputStream getInputStream() throws IOException { * * @return */ + @Override public Charset getCharset() { return charset; } diff --git a/src/test/java/com/github/tobato/fastdfs/controller/FileDownloadDemoController.java b/src/test/java/com/github/tobato/fastdfs/controller/FileDownloadDemoController.java new file mode 100644 index 0000000..29d59e8 --- /dev/null +++ b/src/test/java/com/github/tobato/fastdfs/controller/FileDownloadDemoController.java @@ -0,0 +1,100 @@ +package com.github.tobato.fastdfs.controller; + +import com.github.tobato.fastdfs.service.DefaultGenerateStorageClient; +import org.apache.commons.io.IOUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.*; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletResponse; +import java.net.URLConnection; +import java.nio.charset.StandardCharsets; +import java.util.List; + +/** + * @author MagakiReimu + * @version 1.0 + */ +@RestController +@RequestMapping("/download") +public class FileDownloadDemoController { + + @Autowired + private DefaultGenerateStorageClient defaultGenerateStorageClient; + + @GetMapping("/demo") + public void downloadDemo(@RequestHeader(value = HttpHeaders.RANGE, required = false) String rangeHeader, + HttpServletResponse response) { + // 以下数据,请从自己的开发系统中获取 + String fileName = "abc.txt"; + String group = "group1"; + String path = "xxxxx"; + long fileSize = 0L; + + // 分段下载计算参数 + long rangeStart = 0L; + long rangeEnd = 0L; + long downloadSize = 0L; + long contentLength = fileSize; + + // 获取分段下载请求 + List httpRangeList = HttpRange.parseRanges(rangeHeader); + if (httpRangeList.size() == 0) { + // 无分段下载请求,下载为全量下载 + // 调用downloadFile下载整个文件, 调用接口为只有group, path参数时 + // 下载接口统一, 保持二次调用中rangeStart, downloadSize为0 + // 普通下载状态码200 + response.setStatus(HttpStatus.OK.value()); + } else if (httpRangeList.size() == 1) { + // 分段下载只解析第一个,一般请求只包含一个 + HttpRange httpRange = httpRangeList.get(0); + // 请求起始字节 + rangeStart = httpRange.getRangeStart(fileSize); + // 请求结束字节 + rangeEnd = httpRange.getRangeEnd(fileSize); + // 请求分段长度 + contentLength = rangeEnd - rangeStart + 1L; + // 请求下载长度 + downloadSize = contentLength; + // Content-Range: bytes [unit first byte pos] - [last byte pos] / [entity length] + String contentRange = String.format("bytes %d-%d/%d", rangeStart, rangeEnd, fileSize); + response.addHeader(HttpHeaders.CONTENT_RANGE, contentRange); + // 分段下载状态码206 + response.setStatus(HttpStatus.PARTIAL_CONTENT.value()); + } else { + // 协议支持一个请求包含多段下载, Demo不做多段解析 + response.setStatus(HttpStatus.BAD_REQUEST.value()); + return; + } + + // Content-Type + // 方式一:写死Content-Type + MediaType contentType = MediaType.APPLICATION_OCTET_STREAM; + // 方式二:根据文件名猜测Content-Type + contentType = MediaType.parseMediaType(URLConnection.guessContentTypeFromName(fileName)); + response.setContentType(contentType.toString()); + // Content-Disposition + //@formatter:off + ContentDisposition contentDisposition = ContentDisposition + .builder("form-data") + .name("attachment") + .filename(fileName, StandardCharsets.UTF_8) + .build(); + //@formatter:on + response.addHeader(HttpHeaders.CONTENT_DISPOSITION, contentDisposition.toString()); + // 本次下载大小 + response.setContentLengthLong(contentLength); + // 支持分段下载 + response.addHeader(HttpHeaders.ACCEPT_RANGES, "bytes"); + + // 根据上方计算值,下载区间数据 + defaultGenerateStorageClient.downloadFile(group, path, rangeStart, downloadSize, + inputStream -> { + IOUtils.copy(inputStream, response.getOutputStream()); + return null; + }); + } +}