我正在尝试将3GB文件转换为字节数组,但得到OOM(OutOfMemoryError
)。
我们尝试了
RandomAccessFile randomAccessFile = new RandomAccessFile(sourceLocation.getAbsolutePath(), "r");
MappedByteBuffer mappedByteBuffer = randomAccessFile.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, randomAccessFile.length()); //while it is large file, it threw 'mmap failed: ENOMEM (Out of memory)' exception.
byte[] data = new byte[1024 * 1024]; // 1MB read at time
while (mappedByteBuffer.hasRemaining()) {
int remaining = data.length;
if (mappedByteBuffer.remaining() < remaining)
remaining = mappedByteBuffer.remaining();
mappedByteBuffer.get(data, 0, remaining);
}
mappedByteBuffer.rewind();
byte fileContent[] = mappedByteBuffer.array(); //while it is small file, it threw 'java.nio.ReadOnlyBufferException' exception.
randomAccessFile.close();
}
我的自定义请求正文:我的请求正文准备好的自定义请求正文类
import android.os.Looper;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import okio.BufferedSink;
public class ProgressRequestBodyTemp extends RequestBody
{
private static final int DEFAULT_BUFFER_SIZE = 2048;
private File mFile;
private String mPath;
private String mFileType;
private int mItemIndex;
private UploadCallbacks mListener;
private byte[] encryptedData;
private ByteArrayInputStream bis;
public ProgressRequestBodyTemp(final String fileType, final File file, byte[] encryptedData, final int itemIndex, final UploadCallbacks listener)
{
this.mFile = file;
this.mFileType = fileType;
this.mItemIndex = itemIndex;
this.mListener = listener;
this.encryptedData = encryptedData;
try
{
bis = new ByteArrayInputStream(encryptedData); // Convert byte array into input stream for send data to server
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
@Override
public MediaType contentType()
{
try
{
return MediaType.parse(mFileType);
}
catch (Exception ex)
{
return MediaType.parse("");
}
}
@Override
public long contentLength() throws IOException
{
return encryptedData.length;
}
@Override
public void writeTo(BufferedSink sink) throws IOException
{
long fileLength = mFile.length();
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
long uploaded = 0;
try
{
int read;
android.os.Handler handler = new android.os.Handler(Looper.getMainLooper());
while ((read = bis.read(buffer)) != -1)
{
// update progress on UI thread
handler.post(new ProgressUpdater(uploaded, encryptedData.length));
uploaded += read;
sink.write(buffer, 0, read);
}
}
finally
{
bis.close();
}
}
public interface UploadCallbacks
{
void onProgressUpdate(int itemIndex, int percentage);
}
private class ProgressUpdater implements Runnable
{
private long mUploaded;
private long mTotal;
public ProgressUpdater(long uploaded, long total)
{
mUploaded = uploaded;
mTotal = total;
}
@Override
public void run()
{
if (mListener != null)
{
mListener.onProgressUpdate(mItemIndex, (int) (100 * mUploaded / mTotal));
}
}
}
}
我的请求:请求上传文件
File sourceLocation = new File(".....");
byte fileContent[] = .... // byte array
ProgressRequestBody requestFile = new ProgressRequestBody(fileType, sourceLocation, fileContent, itemIndex, this);
MultipartBody.Part body = MultipartBody.Part.createFormData("file", sourceLocation.getName(), requestFile); // MultipartBody.Part is used to send also the actual file name
Call<String> mediaCall = getRetrofit().addMedia("SOME STRING DATA", body);
它给OutOfMemoryError
,请提出将大文件转换为字节数组的最佳方法。
任何帮助都非常感谢。预先感谢。
答案 0 :(得分:6)
使用InputStream
和OutputStream
。
它们用于处理大量数据,例如,当您使用3GB数据并且无法将其加载到内存中时。
如果您要上传文件,请使用FileInputStream
。创建一个File
对象,将其传递给FileOutputStream
构造函数,并开始将字节从其InputStream
读取到byte[]
缓冲区中,并将带有OutputStream
的字节发送到服务器。
此方法不会导致 OutOfMemoryError
,因为您只读取了足够的字节来填充缓冲区,该字节大约为 2KB-8KB 尺寸。缓冲区已满后,将字节写入服务器。将缓冲区写入服务器后,您将再次读入缓冲区,并且该过程一直持续到整个文件上传完毕。
File file = new File("yourfile.txt");
FileInputStream fis = null;
OutputStream outputStream = null;
url = new URL(desiredUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
try {
fis = new FileInputStream(file);
connection.setDoOutput(true);
outputStream = connection.getOutputStream();
int actuallyRead;
byte[] buffer = new byte[2048];
while ((actuallyRead = fis.read(buffer)) != -1) {
//do something with bytes, for example, write to the server
outputStream.write(buffer);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fis != null)
fis.close();
} catch (IOException ex) {
ex.printStackTrace();
}
try {
if (outputStream != null)
outputStream.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
注意:这种方法并不意味着您需要重新连接到服务器 每次填充缓冲区时。它将不断写入 服务器,直到完成处理文件为止。这一切都会发生 在相同的单一连接下。