尝试将文件上传到JAX-RS(jersey)服务器

13

我正在尝试使用Jersey的multipart/form-data客户端上传文件和其他表单数据。我还使用Jersey上传到REST Web服务。以下是服务器代码:

@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.APPLICATION_JSON)
public String create(@FormDataParam("file") InputStream file,
        @FormDataParam("file") FormDataContentDisposition fileInfo,
        @FormDataParam("name") String name,
        @FormDataParam("description") String description) {
    Ingredient ingredient = new Ingredient();
    ingredient.setName(name);
    ingredient.setDescription(description);
    ingredient.setImageName(fileInfo.getFileName());
    ingredient.setImagePath(context.getRealPath("/resources/uploads/"));
    // TODO save the file.
    try {
        JSONObject json = new JSONObject();
        try {
            ingredientService.create(ingredient);
        } catch (final InvalidParameterException ex) {
            logger.log(Level.INFO, ex.getMessage());
            json.put("result", false);
            json.put("error", ex.getMessage());
            return json.toString();
        } catch (final GoodDrinksException ex) {
            logger.log(Level.WARNING, null, ex);
            json.put("result", false);
            json.put("error", ex.getMessage());
            return json.toString();
        }
        json.put("ingredient", JsonUtil.ingredientToJSON(ingredient));
        return json.put("result", true).toString();
    } catch (JSONException ex) {
        logger.log(Level.SEVERE, null, ex);
        return "{\"result\",false}";
    }
}

我已经在我的台式电脑上使用基本的HTML表单测试了服务器代码,它可以正常工作。问题似乎出现在客户端。这是相关的客户端代码。

ClientConfig config = new DefaultClientConfig();
client = Client.create(config);
client.addFilter(new LoggingFilter());
webResource = client.resource("http://localhost:8080/webapp/resources").path("ingredient");
FormDataMultiPart fdmp = new FormDataMultiPart();
if (file != null) {
    fdmp.bodyPart(new FileDataBodyPart("file", file, MediaType.APPLICATION_OCTET_STREAM_TYPE));
}
fdmp.bodyPart(new FormDataBodyPart("name", ingredient.getName()));
fdmp.bodyPart(new FormDataBodyPart("description", ingredient.getDescription()));

ClientResponse response = webResource.type(MediaType.MULTIPART_FORM_DATA_TYPE).post(ClientResponse.class, fdmp);
String string = response.getEntity(String.class);
logger.log(Level.INFO, "response: {0}", string);

从服务器收到一个400响应,错误信息是:“客户端发送的请求在语法上不正确”

这是日志输出的消息,为了简洁起见,此消息没有附带文件:

1 > POST http://localhost:8080/webapp/resources/ingredient  
1 > Content-Type: multipart/form-data  
1 >   
--Boundary_5_1545082086_1303666703655  
Content-Type: text/plain  
Content-Disposition: form-data;name="name"  
Adam  
--Boundary_5_1545082086_1303666703655  
Content-Type: text/plain  
Content-Disposition: form-data;name="description"  
Test  
--Boundary_5_1545082086_1303666703655--  

我在客户端做错了什么,以致无法正确地使其工作?


示例:http://puspendu.wordpress.com/2012/08/23/restful-webservice-file-upload-with-jersey/ - Puspendu Banerjee
你设置了哪个选项来显示请求正文?我已经启用了 ServerProperties.TRACING = ALLServerProperties.TRACING_THRESHOLD = VERBOSE。但它没有显示请求正文。 - Majid Azimi
4个回答

32

如果您想向 FormDataMultiPart 添加字符串,只需使用与文件附件相同的.field("name", "value")方法即可(queryParam不起作用)。

以下是一个可行的示例:

首先,服务器部分返回读取文件内容的字符串:

@Path("file")
public class FileResource {

    @POST
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    public Response handleUpload(@FormDataParam("file") InputStream stream) throws Exception {
        return Response.ok(IOUtils.toString(stream)).build();
    }
}

其次,客户端方法上传文件:

public void upload(String url, String fileName) {
    InputStream stream = getClass().getClassLoader().getResourceAsStream(fileName);
    FormDataMultiPart part = new FormDataMultiPart().field("file", stream, MediaType.TEXT_PLAIN_TYPE);

    WebResource resource = Client.create().resource(url);
    String response = resource.type(MediaType.MULTIPART_FORM_DATA_TYPE).post(String.class, part);
    assertEquals("Hello, World", response);
}

第三,测试环境:

Server server;

@Before
public void before() throws Exception {
    server = new Server(8080);
    server.addHandler(new WebAppContext(WEB_INF_DIRECTORY, "/"));
    server.start(); 
}

@After
public void after() throws Exception {
    server.stop();
}

@Test
public void upload() {
    upload("http://localhost:8080/file", "file.txt");
}

最后是Maven依赖项:

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.8.2</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>com.sun.jersey</groupId>
        <artifactId>jersey-server</artifactId>
        <version>1.6</version>
    </dependency>
    <dependency>
        <groupId>com.sun.jersey</groupId>
        <artifactId>jersey-client</artifactId>
        <version>1.6</version>
    </dependency>
    <dependency>
        <groupId>com.sun.jersey.contribs</groupId>
        <artifactId>jersey-multipart</artifactId>
        <version>1.6</version>
    </dependency>
    <dependency>
        <groupId>org.mortbay.jetty</groupId>
        <artifactId>jetty-embedded</artifactId>
        <version>6.1.26</version>
    </dependency>
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.0.1</version>
    </dependency>
</dependencies>

file.txt位于类路径的根目录,并包含Hello, World


查询参数主要用于get操作而不是表单选项。问题最终发现是Netbeans 6.9.1中的一个错误,他们没有包含必需的jar文件。唯一的解决方法是升级到Netbeans 7。 - Ruggs
我不明白Netbeans怎么可能是问题的一部分。请看我的更新回复。 - yves amsellem
我没有使用Maven来管理库和Netbeans,当添加jax-rs库时缺少mimepull.jar。你可以在Netbeans bugzilla中找到多个与此问题相关的票号。 - Ruggs
4
预期效果未实现: @FormDataParam("file") InputStream stream 中还包含文件数据周围的分界线和空行! - migu

3

Yves的解决方案在客户端上对我无效。 我查找了一下,发现以下资源:

以上这些资源在我的当前Jersey 1.18中都不能使用(请参见下面的POM提取代码)。 大部分问题出现在客户端。我会收到错误消息,例如:

com.sun.jersey.api.client.ClientHandlerException: javax.ws.rs.WebApplicationException: java.lang.IllegalArgumentException: Missing body part entity of type 'text/plain'
at com.sun.jersey.client.urlconnection.URLConnectionClientHandler.handle(URLConnectionClientHandler.java:155)
at com.sun.jersey.api.client.Client.handle(Client.java:652)
at com.sun.jersey.api.client.WebResource.handle(WebResource.java:682)

这段代码在服务器端快速运行(目前还没有对上传的InputStream进行有趣的处理 - 适合根据需要进行定制)

@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces("text/plain")
public Response uploadFile(
        @FormDataParam("content") final InputStream uploadedInputStream,
        @FormDataParam("fileName") String fileName) throws IOException {
    String uploadContent=IOUtils.toString(uploadedInputStream);
    return Response.ok(uploadContent).build();
}   

客户端将使用以下代码:

客户端将使用以下代码:

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;   
import javax.ws.rs.core.MediaType;

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.multipart.FormDataBodyPart;
import com.sun.jersey.multipart.FormDataMultiPart;
/**
 * upload the given file
 * 
 * inspired by
 * http://neopatel.blogspot.de/2011/04/jersey-posting-multipart-data.html
 * 
 * @param url
 * @param uploadFile
 * @return the result
 * @throws IOException
 */
public String upload(String url, File uploadFile) throws IOException {
    WebResource resource = Client.create().resource(url);
    FormDataMultiPart form = new FormDataMultiPart();
    form.field("fileName", uploadFile.getName());
    FormDataBodyPart fdp = new FormDataBodyPart("content",
            new FileInputStream(uploadFile),
            MediaType.APPLICATION_OCTET_STREAM_TYPE);
    form.bodyPart(fdp);
    String response = resource.type(MediaType.MULTIPART_FORM_DATA).post(String.class, form);
    return response;
}

pom.xml提取:

<properties>
  <jersey.version>1.18</jersey.version>
</properties>

<dependency>
  <groupId>com.sun.jersey</groupId>
  <artifactId>jersey-server</artifactId>
  <version>${jersey.version}</version>
</dependency>
<dependency>
  <groupId>com.sun.jersey</groupId>
  <artifactId>jersey-client</artifactId>
  <version>${jersey.version}</version>
</dependency>
<!--  Multipart support -->
<dependency>
  <groupId>com.sun.jersey.contribs</groupId>
  <artifactId>jersey-multipart</artifactId>
  <version>${jersey.version}</version>
</dependency>

0

public DssResponse callPut(String url, Map<String, String> headers, FileDataBodyPart[] filePath, String boundary, String[] jsonString) throws IOException {
    Client client = ClientBuilder.newClient().register(MultiPartFeature.class);
    WebTarget webTarget = client.target(url);
    Builder builder = webTarget.request(MediaType.MULTIPART_FORM_DATA);
    FormDataMultiPart multiPart = new FormDataMultiPart();
    for (int i = 0; i < filePath.length; i++) {

        if (!filePath[i].getFileEntity().exists()) {
            throw new IOException("Invalid Input File - " + filePath[i].getFileEntity().getAbsolutePath());
        }

        multiPart.bodyPart(new FileDataBodyPart(filePath[i].getName(), filePath[i].getFileEntity()));
    }

    if (boundary != null)
        multiPart.type(Boundary.addBoundary(new MediaType("multipart", "form-data", Collections.singletonMap(Boundary.BOUNDARY_PARAMETER, boundary))));
    for (String jstr : jsonString) {
        multiPart.field("Content-Type", jstr, MediaType.APPLICATION_JSON_TYPE);
    }
    if (headers != null) {
        for (Entry<String, String> header : headers.entrySet()) {
            builder.header(header.getKey(), header.getValue());
            System.out.println(header.getKey() + "===============>>" + header.getValue());
        }
    }

    Response response = builder.accept(MediaType.APPLICATION_JSON).put(Entity.entity(multiPart, multiPart.getMediaType()));

    multiPart.close();

    // Assert.assertNotNull(response);
    if (response == null )
        throw new IOException ("Response is NULL");

    int status = response.getStatus();

    return dssResponse;
}

2
虽然这段代码可能解决问题,但包括解释确实有助于提高您的帖子质量。请记住,您正在回答未来读者的问题,这些人可能不知道您的代码建议原因。 - NathanOliver

-2

或者直接编写一个新文件并上传:

Writer output = null;
    File file = null;
    try {
      String text = "Rajesh Kumar";
      file = new File("write.txt");
      output = new BufferedWriter(new FileWriter(file));
        output.write(text);
        output.close();
    } catch (IOException e) {
        System.out.println("IOException e");
        e.printStackTrace();
    }

    InputStream is = null;

    try {
        is = new FileInputStream(file);
    } catch (FileNotFoundException e) {
        System.out.println("FileNotFoundException e");
        e.printStackTrace();
    } catch (IOException e) {
        System.out.println("IOException e");
        e.printStackTrace();
    }

    FormDataMultiPart part = new FormDataMultiPart().field("file", is, MediaType.TEXT_PLAIN_TYPE);
    res = service.path("rest").path("tenant").path(tenant1.getTenantId()).path("file").type(MediaType.MULTIPART_FORM_DATA_TYPE).post(ClientResponse.class, part);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接