我有3台机器:
文件所在的服务器
运行REST服务的服务器(泽西岛)
可以访问第二台服务器但不能访问第一台服务器的客户端(浏览器)
我如何直接(不将文件保存在第二台服务器上)将文件从第一台服务器下载到客户端计算机?
从第二台服务器可以获取 ByteArrayOutputStream 来从第一台服务器获取文件,我可以使用REST服务将此流进一步传递给客户端吗?
这样行吗?
因此,基本上我想实现的是允许客户端使用第二服务器上的REST服务从第一服务器下载文件(因为没有从客户端到第一服务器的直接访问),而仅使用数据流(因此没有数据接触文件)第二服务器系统)。
我现在尝试使用EasyStream库进行以下操作:
final FTDClient client = FTDClient.getInstance();
try {
final InputStreamFromOutputStream<String> isOs = new InputStreamFromOutputStream<String>() {
@Override
public String produce(final OutputStream dataSink) throws Exception {
return client.downloadFile2(location, Integer.valueOf(spaceId), URLDecoder.decode(filePath, "UTF-8"), dataSink);
}
};
try {
String fileName = filePath.substring(filePath.lastIndexOf("/") + 1);
StreamingOutput output = new StreamingOutput() {
@Override
public void write(OutputStream outputStream) throws IOException, WebApplicationException {
int length;
byte[] buffer = new byte[1024];
while ((length = isOs.read(buffer)) != -1){
outputStream.write(buffer, 0, length);
}
outputStream.flush();
}
};
return Response.ok(output, MediaType.APPLICATION_OCTET_STREAM)
.header("Content-Disposition", "attachment; filename=\"" + fileName + "\"" )
.build();
更新2
因此,现在带有自定义MessageBodyWriter的代码看起来很简单:
ByteArrayOutputStream baos = new ByteArrayOutputStream(2048) ;
client.downloadFile(location, spaceId, filePath, baos);
return Response.ok(baos).build();
但是当尝试使用大文件时,我得到了相同的堆错误。
UPDATE3 终于设法使其正常工作!StreamingOutput达到了目的。
谢谢@peeskillet!非常感谢 !
“如何直接(不将文件保存在第二台服务器上)将文件从第一台服务器下载到客户端计算机?”
只需使用Client
API并InputStream
从响应中获取
Client client = ClientBuilder.newClient();
String url = "...";
final InputStream responseStream = client.target(url).request().get(InputStream.class);
有两种口味可以得到InputStream
。您也可以使用
Response response = client.target(url).request().get();
InputStream is = (InputStream)response.getEntity();
哪一个效率更高?我不确定,但是返回的InputStream
s是不同的类,因此如果您愿意,可以考虑一下。
从第二台服务器可以获取ByteArrayOutputStream来从第一台服务器获取文件,我可以使用REST服务将此流进一步传递给客户端吗?
因此,您在@GradyGCooper提供的链接中看到的大多数答案似乎都倾向于使用StreamingOutput
。一个示例实现可能类似于
final InputStream responseStream = client.target(url).request().get(InputStream.class);
System.out.println(responseStream.getClass());
StreamingOutput output = new StreamingOutput() {
@Override
public void write(OutputStream out) throws IOException, WebApplicationException {
int length;
byte[] buffer = new byte[1024];
while((length = responseStream.read(buffer)) != -1) {
out.write(buffer, 0, length);
}
out.flush();
responseStream.close();
}
};
return Response.ok(output).header(
"Content-Disposition", "attachment, filename=\"...\"").build();
但是,如果我们查看StreamingOutputProvider的源代码,您将在中看到writeTo
,它只是将数据从一个流写入另一个流。因此,在上面的实现中,我们必须写两次。
我们怎样才能只写一个?简单返回InputStream
为Response
final InputStream responseStream = client.target(url).request().get(InputStream.class);
return Response.ok(responseStream).header(
"Content-Disposition", "attachment, filename=\"...\"").build();
如果我们看一下InputStreamProvider的源代码,它只是委托给ReadWriter.writeTo(in, out)
,它只是执行了我们在StreamingOutput
实现中所做的工作
public static void writeTo(InputStream in, OutputStream out) throws IOException {
int read;
final byte[] data = new byte[BUFFER_SIZE];
while ((read = in.read(data)) != -1) {
out.write(data, 0, read);
}
}
助手:
Client
对象是昂贵的资源。您可能要重复使用相同Client
的请求。您可以WebTarget
为每个请求从客户端提取一个。WebTarget target = client.target(url);
InputStream is = target.request().get(InputStream.class);
我认为WebTarget
甚至可以共享。我在Jersey
2.x文档中
找不到任何内容(仅因为它是一个较大的文档,而且我现在懒得扫描它:-),但是在Jersey
1.x文档中却说:Client
并且WebResource
(WebTarget
在2.x中等效)可以在线程之间共享。所以我猜Jersey
2.x会是一样的。但您可能需要自己确认。
您不必使用Client
API。使用java.net
包API 可以轻松实现下载。但是,由于您已经在使用Jersey,因此使用其API不会造成任何伤害
以上是假设Jersey2.x。对于Jersey 1.x,一个简单的Google搜索应会为您提供使用API(或我上面链接到的文档)的大量帮助。
我真是个骗子。虽然OP和我正在考虑如何把一个ByteArrayOutputStream
一个InputStream
的,我错过了简单的解决方案,这是简单地写MessageBodyWriter
为ByteArrayOutputStream
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
@Provider
public class OutputStreamWriter implements MessageBodyWriter<ByteArrayOutputStream> {
@Override
public boolean isWriteable(Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
return ByteArrayOutputStream.class == type;
}
@Override
public long getSize(ByteArrayOutputStream t, Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
return -1;
}
@Override
public void writeTo(ByteArrayOutputStream t, Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
throws IOException, WebApplicationException {
t.writeTo(entityStream);
}
}
然后我们可以简单地ByteArrayOutputStream
在响应中返回
return Response.ok(baos).build();
D’OH!
这是我使用的测试(
资源类别
@Path("test")
public class TestResource {
final String path = "some_150_mb_file";
@GET
@Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response doTest() throws Exception {
InputStream is = new FileInputStream(path);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int len;
byte[] buffer = new byte[4096];
while ((len = is.read(buffer, 0, buffer.length)) != -1) {
baos.write(buffer, 0, len);
}
System.out.println("Server size: " + baos.size());
return Response.ok(baos).build();
}
}
客户测试
public class Main {
public static void main(String[] args) throws Exception {
Client client = ClientBuilder.newClient();
String url = "http://localhost:8080/api/test";
Response response = client.target(url).request().get();
String location = "some_location";
FileOutputStream out = new FileOutputStream(location);
InputStream is = (InputStream)response.getEntity();
int len = 0;
byte[] buffer = new byte[4096];
while((len = is.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
out.flush();
out.close();
is.close();
}
}
因此,对于这个特定的用例的最终解决方案是为OP简单地通过OutputStream
从所述StreamingOutput
的write
方法。似乎是第三方API,需要以aOutputStream
作为参数。
StreamingOutput output = new StreamingOutput() {
@Override
public void write(OutputStream out) {
thirdPartyApi.downloadFile(.., .., .., out);
}
}
return Response.ok(output).build();
不太确定,但是似乎使用ByteArrayOutputStream`在资源方法中进行读/写操作已将某些内容存储到内存中。
downloadFile
方法的接受点OutputStream
是,因此它可以将结果直接写入提供的结果OutputStream
。例如FileOutputStream
,如果您将a写入文件中,而下载即将到来,它将直接流式传输到文件中。
这并不意味着我们要保留对的引用OutputStream
,因为您正尝试使用baos
,这是实现内存的地方。
因此,通过有效的方法,我们将直接写入为我们提供的响应流。该方法write
实际上并没有被调用,直到writeTo
方法(在MessageBodyWriter
),其中OutputStream
传递给它。
看MessageBodyWriter
我写的书,你会得到更好的印象。基本上是在writeTo
方法中,替换ByteArrayOutputStream
使用StreamingOutput
,则内部的方法,呼叫streamingOutput.write(entityStream)
。您可以在答案的前面部分中看到我提供的链接,在该链接中,我指向StreamingOutputProvider
。这正是发生的情况
问题内容: 我需要将文件从服务器下载到桌面。(UBUNTU 10.04)我没有Web访问服务器,只是ssh。 如果有帮助,我的操作系统是Mac OS X和iTerm 2作为终端。 问题答案: 在您的终端中,键入: 相应地替换用户名,主机,远程文件名和本地目录。 如果要访问EC2(或其他需要使用私钥进行身份验证的服务),请使用以下选项:
我想从网站上下载一份PDF。PDF需要在代码中生成,我认为这是freemarker和像iText这样的PDF生成框架的组合。还有更好的办法吗? 然而,主要的问题是,我不知道如何允许用户通过Spring控制器下载文件?
问题内容: 我有一个后端API,它在我们调用它时基本上会下载一个模板。我在html页面上提供了href,因此,每当有人单击该href时,它就会调用后端API,并且应该下载该文件。 但是该文件未下载。 我正在使用React。如果我只是从浏览器访问后端,则文件将被下载,但是如果我从react调用,则不会。 有线索吗? 反应代码: 后端API代码: 问题答案: JS中的请求与访问浏览器中的URL不同。您
后端API代码:
问题内容: 嗨,我有一堆.mp3文件,我想与NSFileManager一起使用并存储在documents文件夹中。有没有一种方法可以在线下载.mp3文件,然后将其保存到documents文件夹?这就是我正在使用的本地文件。 问题答案: 编辑/更新: Xcode 11.5•Swift 5.2 原始答案 Xcode 8.3.2•Swift 3.1
问题内容: 在我的Java应用程序中,我正在使用以下方法从服务器下载文件。 但是此下载非常慢。我该如何快速? 问题答案: 从Java 7开始,您可以下载具有以下内置功能的文件: 对于早期版本,从Java 1.4到Java 6的解决方案是 此代码将URL内容传输到没有任何第三方库的文件。如果仍然很慢,那您就知道这不是附加库的问题,很可能不是Java的问题。至少您在这里没有什么可以改善的。因此,您应该