将大文件转换为Java中的base64表示形式;OutOfMemory异常

将大文件转换为Java中的base64表示形式;OutOfMemory异常,java,base64,Java,Base64,我有一种情况,我需要以这种格式将对象从后端传输到前端: { filename: "filename", type: "type", src: "src", bytes: "base64Representation" } 对象的bytes属性包含存储在远程服务器存储库中的文件的base64表示形式。到目前为止,我一直在处理1-2MB范围内的小文件,将文件转换为相应的base64表示的代码工作正常。但是现在我面临一些大文件的问题,超过100MB。我已经检查了尝试逐块

我有一种情况,我需要以这种格式将对象从后端传输到前端:

{
    filename: "filename",
    type: "type",
    src: "src",
    bytes: "base64Representation"
}

对象的bytes属性包含存储在远程服务器存储库中的文件的base64表示形式。到目前为止,我一直在处理1-2MB范围内的小文件,将文件转换为相应的base64表示的代码工作正常。但是现在我面临一些大文件的问题,超过100MB。我已经检查了尝试逐块转换文件的解决方案,但在过程结束时,我仍然需要将所有块连接在一个字符串中,在这一步,我得到了一个OutOfMemory异常。我也看到了一些使用输出流的建议,但是我不能应用它们,因为我需要上面格式的数据。有人对如何绕过这种情况有什么建议吗?

您可以通过包装
response.getOutputStream()
在servlet中使用OutputStream并动态处理。我将给出一个使用spring boot的工作示例。我测试过了,它能工作

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Base64;

@RestController
public class Base64Controller {

    @RequestMapping(value = "/base64", method = RequestMethod.GET)
    public void getBase64File(HttpServletResponse response) throws IOException {
        response.setContentType("text/plain");
        OutputStream wrap = Base64.getEncoder().wrap(response.getOutputStream());
        FileInputStream fis = new FileInputStream("./temp.txt");
        int bytes;
        byte[] buffer = new byte[2048];
        while ((bytes=fis.read(buffer)) != -1) {
            wrap.write(buffer, 0, bytes);
        }
        fis.close();
        wrap.close();
    }

}

通过包装
response.getOutputStream()
,可以在servlet中动态使用OutputStream和process。我将给出一个使用spring boot的工作示例。我测试过了,它能工作

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Base64;

@RestController
public class Base64Controller {

    @RequestMapping(value = "/base64", method = RequestMethod.GET)
    public void getBase64File(HttpServletResponse response) throws IOException {
        response.setContentType("text/plain");
        OutputStream wrap = Base64.getEncoder().wrap(response.getOutputStream());
        FileInputStream fis = new FileInputStream("./temp.txt");
        int bytes;
        byte[] buffer = new byte[2048];
        while ((bytes=fis.read(buffer)) != -1) {
            wrap.write(buffer, 0, bytes);
        }
        fis.close();
        wrap.close();
    }

}

JSON响应在这里是一个难题,Base64的有效负载为每字节6/8,您可以根据需要多传输33%的数据。实际上,JSON DOM对象将服务器和客户端都拉伸过度

因此,将其转换为简单的二进制下载,并将其流式输出;可能因大数据而被限制


这意味着API发生了变化。

在这里,JSON响应是一个难题,Base64的有效负载为每字节6/8,因此您可以根据需要多传输33%的数据。实际上,JSON DOM对象将服务器和客户端都拉伸过度

因此,将其转换为简单的二进制下载,并将其流式输出;可能因大数据而被限制


这意味着API发生了变化。

我从未使用过struts,所以我不确定这是否可行,但应该是这样的

public class DownloadB64Action extends Action{

    private final static BUFFER_SIZE = 1024;

   @Override
   public ActionForward execute(ActionMapping mapping, ActionForm form,
     HttpServletRequest request, HttpServletResponse response)
     throws Exception {

     response.setContentType("text/plain");

     try 
     {

        FileInputStream in = 
            new FileInputStream(new File("myfile.b64"));

        ServletOutputStream out = Base64.getEncoder().wrap(response.getOutputStream());

        byte[] buffer = new byte[BUFFER_SIZE];

        while(in.read(buffer, 0, BUFFER_SIZE) != -1){
            out.write(buffer, 0, BUFFER_SIZE);
        }
        in.close();
        out.flush();
        out.close();

     }catch(Exception e){
        //TODO handle exception
   }

   return null;
  }
}
为了使它像您所需要的JSON结构一样,您可以尝试在b64有效负载之前直接写入
response.getOutputStream()
“{\”filename\”:\“filename\”,\“type\”:“type\”,\“src\”:\“src\”,\“bytes\”:\”.getBytes(),在b64有效负载之后写入
“\”.getBytes()


}

我从未使用过struts,所以我不确定这是否可行,但应该是这样的

public class DownloadB64Action extends Action{

    private final static BUFFER_SIZE = 1024;

   @Override
   public ActionForward execute(ActionMapping mapping, ActionForm form,
     HttpServletRequest request, HttpServletResponse response)
     throws Exception {

     response.setContentType("text/plain");

     try 
     {

        FileInputStream in = 
            new FileInputStream(new File("myfile.b64"));

        ServletOutputStream out = Base64.getEncoder().wrap(response.getOutputStream());

        byte[] buffer = new byte[BUFFER_SIZE];

        while(in.read(buffer, 0, BUFFER_SIZE) != -1){
            out.write(buffer, 0, BUFFER_SIZE);
        }
        in.close();
        out.flush();
        out.close();

     }catch(Exception e){
        //TODO handle exception
   }

   return null;
  }
}
为了使它像您所需要的JSON结构一样,您可以尝试在b64有效负载之前直接写入
response.getOutputStream()
“{\”filename\”:\“filename\”,\“type\”:“type\”,\“src\”:\“src\”,\“bytes\”:\”.getBytes(),在b64有效负载之后写入
“\”.getBytes()


}

启动JVM时增加堆的大小。@FedericoklezCulloca不同意,因为这个解决方案不会scale@user902383同意(我的意思是)而不是重新组装,为什么不在每次创建块时将块流式传输到前端?如果无法更改HTTP请求的生成方式,则确实必须将对象作为一个整体序列化—上面已经提到了潜在的问题。在启动JVM时增加堆的大小。@FedericoklezCulloca不同意,因为这个解决方案不会scale@user902383同意(我的意思是同意)与其重新组装,不如每次创建一个块时将块流式传输到前端?如果您无法更改HTTP请求的构建方式,那么您确实必须将对象作为一个整体进行序列化——上面已经提到了潜在的问题。