Как написать байтовый массив без OOM? - PullRequest
1 голос
/ 16 июня 2020
• 1000 50 МБ. Ниже мой метод, который я использовал. Итак, может ли кто-нибудь помочь мне улучшить его, чтобы избежать OOM. скачивание. Вышеупомянутые методы возвращают содержимое зашифрованных файлов exoplayer для воспроизведения его содержимого, и я вызываю указанный выше метод следующим образом
ByteArrayDataSource src= new ByteArrayDataSource(decrypt(some_file)); 
Uri uri = new UriByteDataHelper().getUri(decrypt(some_file)); 
DataSpec dataSpec = new DataSpec(uri);  
src.open(dataSpec); 
DataSource.Factory factory = new DataSource.Factory() 
 { 
   @Override public DataSource createDataSource() 
   { 
      return src;  
   } 
 }; 
audioSource = new ProgressiveMediaSource.Factory(factory).createMediaSource(uri);

Ответы [ 3 ]

1 голос
/ 30 июня 2020

Я пишу второй ответ и не редактирую свой первый, так как это совершенно другой подход для решения проблемы.

Когда вы публикуете часть своего кода, я Вы можете видеть, что у вас есть массив байтов с полным и расшифрованным контентом, который воспроизводится exoplayer:

output:
byte[] decrypt(File files)
as input for
ByteArrayDataSource src= new ByteArrayDataSource(decrypt(some_file));

Итак, чтобы избежать двойного и большего потребления памяти при игре с большими файлами (примерно 50 МБ), мой подход это загрузить полный зашифрованный файл и сохранить его в массиве байтов.

На устройствах с хорошей памятью вы можете расшифровать зашифрованный массив байтов за один прогон в другой массив байтов и воспроизвести музыку c из этого расшифрованного массива байтов (этапы 6 + 8 в моем примере программы).

Используя устройство с малым объемом памяти, вы расшифровываете массив байтов кусками (в моей программе - с блоками длиной 16 байтов) и сохраняете расшифрованные фрагменты в то же место в зашифрованном массиве байтов. Когда все фрагменты обработаны, (бывшие) зашифрованные данные теперь расшифровываются, и вы использовали память размером всего в один байтовый массив. Теперь вы можете воспроизвести музыку c из этого байтового массива (шаги 7 + 8).

Для пояснения шаги 1-3 находятся на стороне сервера, а на шагах 3 + 4 происходит передача.

В этом примере используется режим AES CTR , поскольку он обеспечивает одинаковую длину для входных и выходных данных.

В конце я сравниваю байтовые массивы, чтобы доказать, что расшифровка прошла успешно для шагов 6 (прямая расшифровка) и 7 (расшифровка по частям):

вывод:

код:

0 голосов
/ 17 июня 2020

Вам следует подумать о записи расшифрованных данных во временный файл, а затем перезагрузить данные для использования.

Основными причинами ошибки Out of memory являются ByteArrayOutputStream AND byte [] decryptedData = buffer.toByteArray ( ), потому что они оба содержат полные (расшифрованные) данные , и это удваивает потребление памяти вашим методом дешифрования.

Этого можно избежать, расшифровав данные во временный файл в первом step, а затем загрузите данные из временного файла. Я изменил метод дешифрования для обработки дешифрованного выходного потока, а позже появился метод для перезагрузки дешифрованных данных (нет соответствующей обработки исключений, и для моего тестирования я устанавливаю переменную stati c encryptPassword ...).

Осталась только одна часть - вам нужно найти хорошее место для временного файла, а я не Android специалист.

Всего два примечания: вы используете небезопасный режим AES ECB и преобразование строки в байт [] для вашего пароля следует изменить на

.getBytes(StandardCharsets.UTF_8) 

на стороне шифрования и дешифрования, чтобы избежать ошибок, вызванных различными кодировками на разных платформах.

public static void decryptNew(File files, File tempfiles) {
    try (FileInputStream fis = new FileInputStream(files);
         BufferedInputStream in = new BufferedInputStream(fis);
         FileOutputStream out = new FileOutputStream(tempfiles);
         BufferedOutputStream bos = new BufferedOutputStream(out)) {
        byte[] ibuf = new byte[1024];
        int len;
        Cipher cipher = Cipher.getInstance("AES");
        SecretKeySpec sks = new SecretKeySpec(encryptPassword.getBytes(),"AES"); // static password
        // SecretKeySpec sks = new SecretKeySpec(getResources().getString(R.string.encryptPassword).getBytes(),"AES");
        cipher.init(Cipher.DECRYPT_MODE, sks);
        while ((len = in.read(ibuf)) != -1) {
            byte[] obuf = cipher.update(ibuf, 0, len);
            if (obuf != null)
                bos.write(obuf);
        }
        byte[] obuf = cipher.doFinal();
        if (obuf != null)
            bos.write(obuf);
    } catch (BadPaddingException | IllegalBlockSizeException | InvalidKeyException | IOException | NoSuchAlgorithmException | NoSuchPaddingException e) {
        e.printStackTrace();
    }
}

public static byte[] loadFile(File filename) throws IOException {
    byte[] filecontent = new byte[0];
    FileInputStream fileInputStream = null;
    try {
        fileInputStream = new FileInputStream(filename);
        // int byteLength = fff.length();
        // In android the result of file.length() is long
        long byteLength = filename.length(); // byte count of the file-content
        filecontent = new byte[(int) byteLength];
        fileInputStream.read(filecontent, 0, (int) byteLength);
    } catch (IOException e) {
        e.printStackTrace();
        fileInputStream.close();
        return filecontent;
    }
    fileInputStream.close();
    return filecontent;
}

После загрузки содержимого временного файла в массив байтов вы можете удалить файл однострочным (опять же без обработки исключений):

Files.deleteIfExists(tempFile.toPath());
0 голосов
/ 16 июня 2020

Прежде всего, я бы удостоверился, что устройства, на которых это работает, имеют достаточно памяти кучи для запуска этого, возможно, просто программному обеспечению уже выделено много места, и может не остаться намного больше в куче для предоставления ПО. Эта операция не должна требовать много памяти, и я не вижу ничего очевидного, что указывало бы на попытку выделить и неожиданно большой объем памяти. на самом деле просто уменьшение размера массива байтов, какая конкретная причина, почему вы используете 1024? Если возможно, попробуйте:

byte[] d = new byte[8];

Кроме того, если бы это был я, я бы временно сохранял прочитанные данные, возможно, в массиве, и только после завершения чтения шифра я бы позвонил

buffer.write()

Исходя из моего опыта, попытки читать и писать одновременно десятки, о которых не советуют, и могут привести к ряду проблем, по крайней мере, вы должны убедиться, что у вас есть весь шифр и что это действительный один (если у вас есть какие-то требования к проверке) и только затем отправьте его.

Опять же, это не должно быть основной проблемой, устройству кажется, что ему не хватает доступной памяти для выделения, возможно, слишком много зарезервированной памяти для других процессов?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...