All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.dromara.hutool.http.client.body.ResponseBody Maven / Gradle / Ivy
/*
* Copyright (c) 2013-2024 Hutool Team and hutool.cn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.hutool.http.client.body;
import org.dromara.hutool.core.io.IoUtil;
import org.dromara.hutool.core.io.StreamProgress;
import org.dromara.hutool.core.io.file.FileNameUtil;
import org.dromara.hutool.core.io.file.FileUtil;
import org.dromara.hutool.core.io.stream.SyncInputStream;
import org.dromara.hutool.core.lang.Assert;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.http.HttpException;
import org.dromara.hutool.http.HttpGlobalConfig;
import org.dromara.hutool.http.client.Response;
import org.dromara.hutool.http.html.HtmlUtil;
import org.dromara.hutool.http.meta.HeaderName;
import java.io.*;
/**
* 响应体部分封装
*
* @author looly
*/
public class ResponseBody implements HttpBody, Closeable {
private final Response response;
/**
* Http请求原始流
*/
private final SyncInputStream bodyStream;
/**
* 构造
*
* @param response 响应体
* @param in HTTP主体响应流
* @param isAsync 是否异步模式
* @param isIgnoreEOFError 是否忽略EOF错误
*/
public ResponseBody(final Response response, final InputStream in, final boolean isAsync, final boolean isIgnoreEOFError) {
this.response = response;
this.bodyStream = new SyncInputStream(in, response.contentLength(), isAsync, isIgnoreEOFError);
}
@Override
public String contentType() {
return response.header(HeaderName.CONTENT_TYPE);
}
@Override
public long contentLength() {
return response.contentLength();
}
@Override
public InputStream getStream() {
return this.bodyStream;
}
@Override
public void write(final OutputStream out) {
write(out, false, null);
}
/**
* 同步数据到内存,以bytes形式存储
*
* @return this
*/
public ResponseBody sync() {
this.bodyStream.sync();
return this;
}
/**
* 获取响应内容的bytes
*
* @return 响应内容bytes
*/
@Override
public byte[] getBytes() {
return this.bodyStream.readBytes();
}
/**
* 获取响应字符串,自动识别判断编码
*
* @return 响应字符串
*/
public String getString() {
return HtmlUtil.getString(getBytes(), response.charset(), HttpGlobalConfig.isGetCharsetFromContent());
}
/**
* 将响应内容写出到{@link OutputStream}
* 异步模式下直接读取Http流写出,同步模式下将存储在内存中的响应内容写出
* 写出后会关闭Http流(异步模式)
*
* @param out 写出的流
* @param isCloseOut 是否关闭输出流
* @param streamProgress 进度显示接口,通过实现此接口显示下载进度
* @return 写出bytes数
* @since 3.3.2
*/
public long write(final OutputStream out, final boolean isCloseOut, final StreamProgress streamProgress) {
Assert.notNull(out, "[out] must be not null!");
try {
return this.bodyStream.copyTo(out, streamProgress);
} finally {
if (isCloseOut) {
IoUtil.closeQuietly(out);
}
}
}
/**
* 将响应内容写出到文件
*
* @param targetFileOrDir 写出到的文件或目录的路径
* @return 写出的文件
*/
public File write(final String targetFileOrDir) {
return write(FileUtil.file(targetFileOrDir));
}
/**
* 将响应内容写出到文件
* 异步模式下直接读取Http流写出,同步模式下将存储在内存中的响应内容写出
* 写出后会关闭Http流(异步模式)
*
* @param targetFileOrDir 写出到的文件或目录
* @return 写出的文件
*/
public File write(final File targetFileOrDir) {
return write(targetFileOrDir, null);
}
/**
* 将响应内容写出到文件-避免未完成的文件
* 来自:https://gitee.com/dromara/hutool/pulls/407
* 此方法原理是先在目标文件同级目录下创建临时文件,下载之,等下载完毕后重命名,避免因下载错误导致的文件不完整。
*
* @param targetFileOrDir 写出到的文件或目录
* @param streamProgress 进度显示接口,通过实现此接口显示下载进度
* @return 写出的文件对象
*/
public File write(final File targetFileOrDir, final StreamProgress streamProgress) {
return write(targetFileOrDir, null, streamProgress);
}
/**
* 将响应内容写出到文件-避免未完成的文件
* 来自:https://gitee.com/dromara/hutool/pulls/407
* 此方法原理是先在目标文件同级目录下创建临时文件,下载之,等下载完毕后重命名,避免因下载错误导致的文件不完整。
*
* @param targetFileOrDir 写出到的文件或目录
* @param tempFileSuffix 临时文件后缀,默认".temp"
* @param streamProgress 进度显示接口,通过实现此接口显示下载进度
* @return 写出的文件对象
* @since 5.7.12
*/
public File write(final File targetFileOrDir, final String tempFileSuffix, final StreamProgress streamProgress) {
Assert.notNull(targetFileOrDir, "[targetFileOrDir] must be not null!");
File outFile = getTargetFile(targetFileOrDir, null);
// 目标文件真实名称
final String fileName = outFile.getName();
// 临时文件
outFile = new File(outFile.getParentFile(), FileNameUtil.addTempSuffix(fileName, tempFileSuffix));
try {
outFile = writeDirect(outFile, null, streamProgress);
// 重命名下载好的临时文件
return FileUtil.rename(outFile, fileName, true);
} catch (final Throwable e) {
// 异常则删除临时文件
FileUtil.del(outFile);
throw new HttpException(e);
}
}
/**
* 将响应内容直接写出到文件,目标为目录则从Content-Disposition中获取文件名
*
* @param targetFileOrDir 写出到的文件
* @param customParamName 自定义的Content-Disposition中文件名的参数名
* @param streamProgress 进度显示接口,通过实现此接口显示下载进度
* @return 写出的文件
*/
public File writeDirect(final File targetFileOrDir, final String customParamName, final StreamProgress streamProgress) {
Assert.notNull(targetFileOrDir, "[targetFileOrDir] must be not null!");
final File outFile = getTargetFile(targetFileOrDir, customParamName);
write(FileUtil.getOutputStream(outFile), true, streamProgress);
return outFile;
}
@Override
public void close() throws IOException {
this.bodyStream.close();
}
@Override
public String toString() {
return getString();
}
// region ---------------------------------------------------------------------------- Private Methods
/**
* 从响应头补全下载文件名,返回补全名称后的文件
*
* @param targetFileOrDir 目标文件夹或者目标文件
* @param customParamName 自定义的参数名称,如果传入{@code null},默认使用"filename"
* @return File 保存的文件
* @since 5.4.1
*/
private File getTargetFile(final File targetFileOrDir, final String customParamName) {
if (!targetFileOrDir.isDirectory()) {
// 非目录直接返回
return targetFileOrDir;
}
// 从头信息中获取文件名
final String fileName = response.getFileNameFromDisposition(customParamName);
if (StrUtil.isBlank(fileName)) {
throw new HttpException("Can`t get file name from [Content-Disposition]!");
}
return FileUtil.file(targetFileOrDir, fileName);
}
// endregion ---------------------------------------------------------------------------- Private Methods
}