【问题标题】:Java: download CSV file with REST serviceJava:使用 REST 服务下载 CSV 文件
【发布时间】:2017-11-16 02:18:47
【问题描述】:

我正在尝试从 REST 端点下载 csv 文件。这是我正在尝试的。

@ApiOperation(value = "export",
        notes = "Export Cache details for a given criteria")
@ApiImplicitParams({

})
@ApiResponses(value = {
        @ApiResponse(code = 400, message = "Bad Request"),
        @ApiResponse(code = 404, message = "Not Found"),
        @ApiResponse(code = 500, message = "Internal Server Error") })
@RequestMapping(method = RequestMethod.GET, value = "/export")
public ResponseEntity export( HttpServletRequest request )
{
    CacheDataManager cacheResultHandler = new CacheDataManager();
    InputStreamResource inputStreamResource = null;
    HttpHeaders httpHeaders = new HttpHeaders();
    long contentLengthOfStream;

    try
    {
        inputStreamResource = cacheResultHandler.exportCacheResults( request );
        httpHeaders.set( HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + "test.csv" );

        contentLengthOfStream = inputStreamResource.contentLength();
        httpHeaders.setContentLength( contentLengthOfStream );
    }
    catch ( IOException e )
    {
        e.printStackTrace();
    }

    return new ResponseEntity( inputStreamResource, httpHeaders, HttpStatus.OK );

}

我的导出功能。

@Override
public InputStreamResource export( HttpServletRequest request )
{
    StringBuilder sb = new StringBuilder();
    StringBuilder fileName = new StringBuilder( VALIDATION_REPORT );

    sb.append( "Column A" );
    sb.append( "," );
    sb.append( "Column B" );
    sb.append( "\n" );

    try
    {

            sb.append( "TEST A");
            sb.append( ',' );
            sb.append( "TEST B" );
            sb.append( '\n' );



        fileName.append( "_" ).append( sdf.format( new Date() ) ).append( ".csv" );
        return CsvFileWriter.csvFileWrite( fileName.toString(), sb );

    }
    catch ( Exception e )
    {
        e.printStackTrace();

    }

    return null;
}

CsvFileWriter.java

package it.app.ext.dashboard.util;

import org.springframework.core.io.InputStreamResource;

import java.io.*;

public class CsvFileWriter
{

public static InputStreamResource csvFileWrite( String fileName, StringBuilder content ) throws FileNotFoundException
{
    File file = null;
    PrintWriter pw = null;
    try
    {
        file = new File( fileName );
        pw = new PrintWriter( file );
        pw.write( content.toString() );
    }
    catch ( FileNotFoundException e )
    {
        e.printStackTrace();
    }
    finally
    {
        pw.flush();
        pw.close();
    }

    InputStream inputStream = new FileInputStream( file );

    return new InputStreamResource( inputStream );
}

}

文件正在生成,内容包含在 tomcat/bin 文件夹中,但出现异常。

java.lang.IllegalStateException: InputStream has already been read - do not use InputStreamResource if a stream needs to be read multiple times.

我想在调用此端点后下载一个 .csv 文件。

欢迎提出任何建议。

谢谢你

【问题讨论】:

    标签: java spring rest csv spring-mvc


    【解决方案1】:

    说明:

    你首先得到了 inputStream:

    contentLengthOfStream =inputStreamResource.contentLength();

    然后Spring的returnValueHandlers又得到了inputStream:

    new ResponseEntity( inputStreamResource, httpHeaders, HttpStatus.OK ).

    但是inputStreamResource包裹的inputStream只能使用一次:

    /**
     * This implementation throws IllegalStateException if attempting to
     * read the underlying stream multiple times.
     */
    public InputStream getInputStream() throws IOException, IllegalStateException {
        if (this.read) {
            throw new IllegalStateException("InputStream has already been read - " +
                    "do not use InputStreamResource if a stream needs to be read multiple times");
        }
        this.read = true;
        return this.inputStream;
    }
    

    解决方案:可以从 inputStream 中获取字节并返回带有字节的 ResponseEntity。

    @ApiOperation(value = "export",
            notes = "Export Cache details for a given criteria")
    @ApiImplicitParams({
    
    })
    @ApiResponses(value = {
            @ApiResponse(code = 400, message = "Bad Request"),
            @ApiResponse(code = 404, message = "Not Found"),
            @ApiResponse(code = 500, message = "Internal Server Error") })
    @RequestMapping(method = RequestMethod.GET, value = "/export")
    public ResponseEntity export( HttpServletRequest request )
    {
        CacheDataManager cacheResultHandler = new CacheDataManager();
        InputStreamResource inputStreamResource = null;
        InputStream inputStream = null;
        byte[] byteArray = new byte[0];
        HttpHeaders httpHeaders = new HttpHeaders();
    
        try
        {
            inputStreamResource = cacheResultHandler.exportCacheResults( request );
            httpHeaders.set( HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + "test.csv" );
    
            //convert inputStream to bytes
            inputStream = inputStreamResource.getInputStream();
            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
            int nRead;
            byte[] data = new byte[1024];
            while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
                buffer.write(data, 0, nRead);
            }
    
            buffer.flush();
            byteArray = buffer.toByteArray();
    
            httpHeaders.setContentLength(byteArray.length);
        }
        catch ( IOException e )
        {
            e.printStackTrace();
        }
    
        return new ResponseEntity( byteArray, httpHeaders, HttpStatus.OK );
    
    }
    

    建议:使用Apache Commons IO将InputStream转换为字节。需要添加一个lib依赖,可以让你的代码简洁

    byte[] byteArray = IOUtils.toByteArray(inputStream);
    

    【讨论】:

    • 这是有效的,因为响应正文包含 响应正文下载 exportCacheResults?pr=HTL&index=0&size=100。单击此 url 后,它会下载 application-octet-stream-exportCacheResults-pr=HTL&index=0&size=100-blob-http---localhost-8080-d541c19c-6da3-478e-b8ee-0448aa5bf598 的文件名。这是一个类型为 File 的文件。怎么下载.csv文件
    • 我看不到任何与您的网址“exportCacheResults?pr=HTL&index=0&size=100”相关的代码。添加httpHeaders.set("Content-Disposition", "attachment; filename=" + "test.csv" )时,可以直接下载.csv文件!
    【解决方案2】:

    不要重复使用同一个文件,使用单独的代码返回InputStream:

    return new InputStreamResource( new FileInputStream( new File( fileName ) ) );

    【讨论】:

    • 我改变了这些 InputStream inputStream = new FileInputStream(file); return new InputStreamResource( inputStream ); 两行到你 seggested 的内容。但是错误和以前一样。
    • @Rose18 应该创建新的文件对象实例:InputStream inputStream = new FileInputStream( new File( fileName ) ) 而不是重用旧文件对象
    • 对不起,我没听懂你说的。我需要更改哪个文件?
    • @Rose18 你可以看到我的答案,它会帮助你弄清楚!
    • pw.close(); 行将关闭您的 file 对象。我的意思是`file = new File(fileName);`不能设置为新对象:`InputStream inputStream = new FileInputStream(file);`所以请再次创建file对象的实例:file = new File( fileName ); InputStream inputStream = new FileInputStream( file ); return new InputStreamResource( inputStream );
    猜你喜欢
    • 1970-01-01
    • 2012-07-02
    • 1970-01-01
    • 2015-06-25
    • 2020-10-28
    • 2016-01-04
    • 2013-06-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多