【发布时间】:2020-05-18 17:21:11
【问题描述】:
我有一个应用程序需要将大文件(最多几 GB)从客户端保存到服务器。 我遇到的问题是服务器的最大堆大小为 2 GB,而且我经常收到“OutOfMemoryError”。
目前我有这段代码(我知道这不是一个好方法,并且文件完全加载到服务器堆):
Angular 组件 ts:
public void uploadFile() {
let body = new FormData();
body.append("file", this.file, this.fileName);
this.http.post("/uploadFile", body).subscribe(res => {
alert("success");
});
}
弹簧控制器:
@RequestMapping(value = "/uploadFile", method = RequestMethod.POST)
@ResponseBody
public void uploadFile(@RequestParam("file") MultipartFile file) {
SimpleDDateFormat formatter = new SimpleDDateFormat("dd-mm-yyyy-hh-mm-ss");
String filename = FILE_DIR_LOCATION + file.getOriginalFileName() + formater.format(new Date());
InputStream in = new BufferedInputStream(file.getInputStream());
FileOutputStream fos = new FileOutputStream(filename);
byte[] buffer = new byte[4096];
int readedBytes = 0;
while ((readedBytes = in.read(buffer)) != -1) {
fos.write(buffer, 0, readedBytes);
fos.flush;
}
fos.close();
}
我想在客户端将文件分成更小的部分,然后像这样将它们发送到服务器,这样可以再次将它们保存为一个文件,而不会占用太多堆。 因为可能同时有多个客户端,并且一个文件可能比堆大。 我找不到这样做的最佳做法,我想到了这样的事情:
Angular 组件 ts:
static MAX_PART_SIZE = 4096;
public void uploadFile() {
this.fileReader = new FileReader();
this.fileReader.readAsArrayBuffer(this.file);
}
public void startSendingFile() {
this.bytesArray = this.fileReader.result;
this.http("uploadFirstPart", //send only the MAX_PART_SIZE to the server, size and name).subscribe(res => {
if (this.bytesArray.length > MAX_PART_SIZE) {
this.keepSendingToServer(res, this.bytesArray.length - MAX_PART_SIZE);
}
});
}
public void keepSendingToServer(fileName, remainBytes) {
this.http("uploadPart", //send the next part of the file and if this is the end of the file).subscribe(res => {
if (remainBytes - MAX_PART_SIZE > 0) {
this.keepSendingToServer(fileName, remainBytes - MAX_PART_SIZE);
}
});
}
弹簧控制器:
Static HashMap<String, FileOutputStream> files = new HashMap<String, FileOutputStream>();
@RequestMapping(value = "/uploadFirstPart", method = RequestMethod.POST)
@ResponseBody
public String uploadFile(@RequestParam("file") byte[] file, @RequestParam("filename") String name, @RequestParam("size") int size) {
SimpleDDateFormat formatter = new SimpleDDateFormat("dd-mm-yyyy-hh-mm-ss");
String filename = FILE_DIR_LOCATION + name + formater.format(new Date());
FileOutputStream fos = new FileOutputStream(filename);
files.put(filename, fos);
fos.write(file, 0, size);
fos.flush;
return filename;
}
@RequestMapping(value = "/uploadPart", method = RequestMethod.POST)
@ResponseBody
public void uploadPart(@RequestParam("file") byte[] file, @RequestParam("filename") String name, @RequestParam("size") int size, @RequestParam("isEnd") boolean isEnd) {
FileOutputStream fos = files.get(name);
fos.write(file, 0, size);
fos.flush;
if (isEnd) {
fos.close();
files.remove(name);
}
}
一些问题: 1. 这是最佳实践吗?还有其他方法吗? 2.你能帮我修复代码中的cmets,因为我找不到如何做的事情。 3. 有没有办法只获取想要的字节而不在客户端加载整个大文件(如java中的输入流)? 4. 在我的服务器中,有一个文件以base64字符串发送,另一个以byte[]发送,有没有更好的发送文件方式(文件可以是图像、音频、视频、二进制等)?
【问题讨论】:
标签: angular spring spring-boot file