diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/ChunkedFileUploadRemoteOperation.java b/library/src/main/java/com/owncloud/android/lib/resources/files/ChunkedFileUploadRemoteOperation.java deleted file mode 100644 index 818c4e0e4a..0000000000 --- a/library/src/main/java/com/owncloud/android/lib/resources/files/ChunkedFileUploadRemoteOperation.java +++ /dev/null @@ -1,340 +0,0 @@ -/* - * Nextcloud Android Library - * - * SPDX-FileCopyrightText: 2015 ownCloud Inc. - * SPDX-License-Identifier: MIT - */ -package com.owncloud.android.lib.resources.files; - -import android.text.TextUtils; - -import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.network.ChunkFromFileChannelRequestEntity; -import com.owncloud.android.lib.common.network.ProgressiveDataTransfer; -import com.owncloud.android.lib.common.network.WebdavEntry; -import com.owncloud.android.lib.common.network.WebdavUtils; -import com.owncloud.android.lib.common.operations.OperationCancelledException; -import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import com.owncloud.android.lib.common.utils.Log_OC; - -import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler; -import org.apache.commons.httpclient.methods.PutMethod; -import org.apache.commons.httpclient.params.HttpMethodParams; -import org.apache.jackrabbit.webdav.DavConstants; -import org.apache.jackrabbit.webdav.MultiStatus; -import org.apache.jackrabbit.webdav.MultiStatusResponse; -import org.apache.jackrabbit.webdav.client.methods.MkColMethod; -import org.apache.jackrabbit.webdav.client.methods.MoveMethod; -import org.apache.jackrabbit.webdav.client.methods.PropFindMethod; - -import java.io.File; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.nio.channels.FileChannel; -import java.util.Locale; -import java.util.Objects; - -import androidx.annotation.VisibleForTesting; - - -public class ChunkedFileUploadRemoteOperation extends UploadFileRemoteOperation { - - public static final long CHUNK_SIZE_MOBILE = 10240000; - public static final long CHUNK_SIZE_WIFI = 40960000; - public static final String DESTINATION_HEADER = "Destination"; - public static final int CHUNK_NAME_LENGTH = 6; - private static final String TAG = ChunkedFileUploadRemoteOperation.class.getSimpleName(); - public final int ASSEMBLE_TIME_MIN = 30 * 1000; // 30s - public final int ASSEMBLE_TIME_MAX = 30 * 60 * 1000; // 30min - public final int ASSEMBLE_TIME_PER_GB = 3 * 60 * 1000; // 3 min - private final boolean onWifiConnection; - private String uploadFolderUri; - private String destinationUri; - - public ChunkedFileUploadRemoteOperation(String storagePath, - String remotePath, - String mimeType, - String requiredEtag, - long lastModificationTimestamp, - boolean onWifiConnection) { - this(storagePath, remotePath, mimeType, requiredEtag, lastModificationTimestamp, onWifiConnection, null); - } - - public ChunkedFileUploadRemoteOperation(String storagePath, - String remotePath, - String mimeType, - String requiredEtag, - long lastModificationTimestamp, - Long creationTimestamp, - boolean onWifiConnection, - boolean disableRetries) { - this(storagePath, - remotePath, - mimeType, - requiredEtag, - lastModificationTimestamp, - onWifiConnection, - null, - creationTimestamp, - disableRetries); - } - - public ChunkedFileUploadRemoteOperation(String storagePath, - String remotePath, - String mimeType, - String requiredEtag, - long lastModificationTimestamp, - boolean onWifiConnection, - String token) { - this(storagePath, - remotePath, - mimeType, - requiredEtag, - lastModificationTimestamp, - onWifiConnection, - token, - null, - true); - } - - public ChunkedFileUploadRemoteOperation(String storagePath, - String remotePath, - String mimeType, - String requiredEtag, - long lastModificationTimestamp, - boolean onWifiConnection, - String token, - Long creationTimestamp, - boolean disableRetries) { - super(storagePath, - remotePath, - mimeType, - requiredEtag, - lastModificationTimestamp, - creationTimestamp, - token, - disableRetries); - this.onWifiConnection = onWifiConnection; - } - - protected static Chunk calcNextChunk(long fileSize, int chunkId, long startByte, long chunkSize) { - if (chunkId < 0 || String.valueOf(chunkId).length() > CHUNK_NAME_LENGTH) { - throw new IllegalArgumentException( - "chunkId must not exceed length specified in CHUNK_NAME_LENGTH (" + CHUNK_NAME_LENGTH + ")"); - } - - long length = startByte + chunkSize > fileSize ? fileSize - startByte : chunkSize; - return new Chunk(chunkId, startByte, length); - } - - @Override - protected RemoteOperationResult run(OwnCloudClient client) { - RemoteOperationResult result; - DefaultHttpMethodRetryHandler oldRetryHandler = (DefaultHttpMethodRetryHandler) client.getParams() - .getParameter(HttpMethodParams.RETRY_HANDLER); - File file = new File(localPath); - MoveMethod moveMethod = null; - try { - if (disableRetries) { - // prevent that uploads are retried automatically by network library - client.getParams() - .setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(0, false)); - } - - // chunk length - long chunkSize; - if (onWifiConnection) { - chunkSize = CHUNK_SIZE_WIFI; - } else { - chunkSize = CHUNK_SIZE_MOBILE; - } - - uploadFolderUri = client.getUploadUri() + "/" + client.getUserId() + "/" + FileUtils.md5Sum(file); - - destinationUri = client.getDavUri() + "/files/" + client.getUserId() + WebdavUtils.encodePath(remotePath); - - // create folder - MkColMethod createFolder = new MkColMethod(uploadFolderUri); - - createFolder.addRequestHeader(DESTINATION_HEADER, destinationUri); - - client.executeMethod(createFolder, 30000, 5000); - - // list chunks - PropFindMethod listChunks = new PropFindMethod(uploadFolderUri, - WebdavUtils.getChunksPropSet(), - DavConstants.DEPTH_1); - - client.executeMethod(listChunks); - - if (!listChunks.succeeded()) { - return new RemoteOperationResult(listChunks.succeeded(), listChunks); - } - - MultiStatus dataInServer = listChunks.getResponseBodyAsMultiStatus(); - - // determine chunks already on server - // chunks are assumed to be uploaded linearly, starting at 0B - long nextByte = 0; - int lastId = 0; - for (MultiStatusResponse response : dataInServer.getResponses()) { - WebdavEntry we = new WebdavEntry(response, Objects.requireNonNull(client.getUploadUri().getPath())); - String name = we.getName(); - - // filter out any objects not matching expected chunk name - if (!we.isDirectory() && name != null && (name.length() <= CHUNK_NAME_LENGTH) && - TextUtils.isDigitsOnly(name)) { - // is part of upload - int id = Integer.parseInt(name); - if (id > lastId) { - lastId = id; - } - nextByte += we.getContentLength(); - } - } - - // iteratively upload remaining chunks - while (nextByte + 1 < file.length()) { - // determine size of next chunk - Chunk chunk = calcNextChunk(file.length(), ++lastId, nextByte, chunkSize); - - RemoteOperationResult chunkResult = uploadChunk(client, chunk); - if (!chunkResult.isSuccess()) { - return chunkResult; - } - - if (cancellationRequested.get()) { - return new RemoteOperationResult(new OperationCancelledException()); - } - - nextByte += chunk.getLength(); - } - - // assemble - String originUri = uploadFolderUri + "/.file"; - - moveMethod = new MoveMethod(originUri, destinationUri, true); - moveMethod.addRequestHeader(OC_X_OC_MTIME_HEADER, String.valueOf(lastModificationTimestamp)); - - if (creationTimestamp != null && creationTimestamp > 0) { - moveMethod.addRequestHeader(OC_X_OC_CTIME_HEADER, String.valueOf(creationTimestamp)); - } - - if (token != null) { - moveMethod.addRequestHeader(E2E_TOKEN, token); - } - - final int DO_NOT_CHANGE_DEFAULT = -1; - int moveResult = client.executeMethod(moveMethod, calculateAssembleTimeout(file), DO_NOT_CHANGE_DEFAULT); - - result = new RemoteOperationResult(isSuccess(moveResult), moveMethod); - } catch (Exception e) { - if (putMethod != null && putMethod.isAborted()) { - if (cancellationRequested.get() && cancellationReason != null) { - result = new RemoteOperationResult(cancellationReason); - } else { - result = new RemoteOperationResult(new OperationCancelledException()); - } - } else if (moveMethod != null && moveMethod.isAborted()) { - if (cancellationRequested.get() && cancellationReason != null) { - result = new RemoteOperationResult(cancellationReason); - } else { - result = new RemoteOperationResult(new OperationCancelledException()); - } - } else { - result = new RemoteOperationResult(e); - } - } finally { - if (disableRetries) { - // reset previous retry handler - client.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, oldRetryHandler); - } - } - return result; - } - - private RemoteOperationResult uploadChunk(OwnCloudClient client, Chunk chunk) throws IOException { - int status; - RemoteOperationResult result; - - FileChannel channel = null; - RandomAccessFile raf = null; - - File file = new File(localPath); - - try { - raf = new RandomAccessFile(file, "r"); - channel = raf.getChannel(); - entity = new ChunkFromFileChannelRequestEntity(channel, - mimeType, - chunk.getStart(), - chunk.getLength(), - file); - - synchronized (dataTransferListeners) { - ((ProgressiveDataTransfer) entity).addDataTransferProgressListeners(dataTransferListeners); - } - - // pad chunk name to 6 digits - String chunkUri = - uploadFolderUri + "/" + String.format(Locale.ROOT, "%0" + CHUNK_NAME_LENGTH + "d", chunk.getId()); - - if (putMethod != null) { - putMethod.releaseConnection(); // let the connection available for other methods - } - - putMethod = createPutMethod(chunkUri); - - putMethod.addRequestHeader(DESTINATION_HEADER, destinationUri); - - if (token != null) { - putMethod.addRequestHeader(E2E_TOKEN, token); - } - - status = client.executeMethod(putMethod); - - result = new RemoteOperationResult(isSuccess(status), putMethod); - - client.exhaustResponse(putMethod.getResponseBodyAsStream()); - Log_OC.d(TAG, - "Upload of " + localPath + " to " + remotePath + ", chunk id: " + chunk.getId() + " from " + - chunk.getStart() + " size: " + chunk.getLength() + ", HTTP result status " + status); - } finally { - if (channel != null) { - try { - channel.close(); - } catch (IOException e) { - Log_OC.e(TAG, "Error closing file channel!", e); - } - } - if (raf != null) { - try { - raf.close(); - } catch (IOException e) { - Log_OC.e(TAG, "Error closing file access!", e); - } - } - if (putMethod != null) { - putMethod.releaseConnection(); // let the connection available for other methods - } - } - return result; - } - - private PutMethod createPutMethod(String uriPrefix) { - putMethod = new PutMethod(uriPrefix); - putMethod.setRequestEntity(entity); - if (cancellationRequested.get()) { - putMethod.abort(); // next method will throw an exception - } - - return putMethod; - } - - @VisibleForTesting - public int calculateAssembleTimeout(File file) { - final double fileSizeInGb = file.length() / 1e9; - - return Math.max(ASSEMBLE_TIME_MIN, Math.min((int) (ASSEMBLE_TIME_PER_GB * fileSizeInGb), ASSEMBLE_TIME_MAX)); - } -} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/ChunkedFileUploadRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/ChunkedFileUploadRemoteOperation.kt new file mode 100644 index 0000000000..34744a7447 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/ChunkedFileUploadRemoteOperation.kt @@ -0,0 +1,354 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-FileCopyrightText: 2015 ownCloud Inc. + * SPDX-License-Identifier: MIT + */ +package com.owncloud.android.lib.resources.files + +import androidx.annotation.VisibleForTesting +import androidx.core.text.isDigitsOnly +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.network.ChunkFromFileChannelRequestEntity +import com.owncloud.android.lib.common.network.ProgressiveDataTransfer +import com.owncloud.android.lib.common.network.WebdavEntry +import com.owncloud.android.lib.common.network.WebdavUtils +import com.owncloud.android.lib.common.operations.OperationCancelledException +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.common.utils.Log_OC +import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler +import org.apache.commons.httpclient.methods.PutMethod +import org.apache.commons.httpclient.params.HttpMethodParams +import org.apache.jackrabbit.webdav.DavConstants +import org.apache.jackrabbit.webdav.MultiStatus +import org.apache.jackrabbit.webdav.client.methods.MkColMethod +import org.apache.jackrabbit.webdav.client.methods.MoveMethod +import org.apache.jackrabbit.webdav.client.methods.PropFindMethod +import java.io.Closeable +import java.io.File +import java.io.IOException +import java.io.RandomAccessFile +import java.nio.channels.FileChannel +import java.util.Locale +import kotlin.math.max +import kotlin.math.min + +@Suppress("LongParameterList") +class ChunkedFileUploadRemoteOperation + @JvmOverloads + constructor( + storagePath: String?, + remotePath: String?, + mimeType: String?, + requiredEtag: String?, + lastModificationTimestamp: Long, + private val onWifiConnection: Boolean, + token: String? = null, + creationTimestamp: Long? = null, + disableRetries: Boolean = true, + private val serverMaxChunkSize: Long = SERVER_MAX_CHUNK_SIZE_UNKNOWN + ) : UploadFileRemoteOperation( + storagePath, + remotePath, + mimeType, + requiredEtag, + lastModificationTimestamp, + creationTimestamp, + token, + disableRetries + ) { + // Assemble timeouts, in milliseconds. The literals are the definition itself, hence the MagicNumber opt-out. + @Suppress("MagicNumber") + @JvmField + val assembleTimeMin: Int = 30 * 1000 // 30s + + @Suppress("MagicNumber") + @JvmField + val assembleTimeMax: Int = 30 * 60 * 1000 // 30min + + @Suppress("MagicNumber") + @JvmField + val assembleTimePerGB: Int = 3 * 60 * 1000 // 3 min + + private lateinit var uploadFolderUri: String + private lateinit var destinationUri: String + private var moveMethod: MoveMethod? = null + + @JvmOverloads + constructor( + storagePath: String?, + remotePath: String?, + mimeType: String?, + requiredEtag: String?, + lastModificationTimestamp: Long, + creationTimestamp: Long?, + onWifiConnection: Boolean, + disableRetries: Boolean, + serverMaxChunkSize: Long = SERVER_MAX_CHUNK_SIZE_UNKNOWN + ) : this( + storagePath, + remotePath, + mimeType, + requiredEtag, + lastModificationTimestamp, + onWifiConnection, + null, + creationTimestamp, + disableRetries, + serverMaxChunkSize + ) + + @Suppress("TooGenericExceptionCaught") + override fun run(client: OwnCloudClient): RemoteOperationResult { + val oldRetryHandler = + client.params + .getParameter(HttpMethodParams.RETRY_HANDLER) as? DefaultHttpMethodRetryHandler + + return try { + if (disableRetries) { + // prevent that uploads are retried automatically by network library + client.params.setParameter(HttpMethodParams.RETRY_HANDLER, DefaultHttpMethodRetryHandler(0, false)) + } + + uploadAndAssemble(client) + } catch (e: Exception) { + cancelledOrFailed(e) + } finally { + if (disableRetries) { + // reset previous retry handler + client.params.setParameter(HttpMethodParams.RETRY_HANDLER, oldRetryHandler) + } + } + } + + private fun uploadAndAssemble(client: OwnCloudClient): RemoteOperationResult { + val file = File(localPath) + val userId = client.userId + + uploadFolderUri = "${client.uploadUri}/$userId/${FileUtils.md5Sum(file)}" + destinationUri = "${client.davUri}/files/$userId${WebdavUtils.encodePath(remotePath)}" + + createUploadFolder(client) + + val listChunks = PropFindMethod(uploadFolderUri, WebdavUtils.getChunksPropSet(), DavConstants.DEPTH_1) + client.executeMethod(listChunks) + + if (!listChunks.succeeded()) { + return RemoteOperationResult(false, listChunks) + } + + val uploaded = uploadedChunks(client, listChunks.responseBodyAsMultiStatus) + + return uploadRemainingChunks(client, file, uploaded) ?: assemble(client, file) + } + + private fun createUploadFolder(client: OwnCloudClient) { + val createFolder = MkColMethod(uploadFolderUri) + createFolder.addRequestHeader(DESTINATION_HEADER, destinationUri) + client.executeMethod(createFolder, CREATE_FOLDER_READ_TIMEOUT, DO_NOT_CHANGE_DEFAULT) + } + + private fun uploadedChunks( + client: OwnCloudClient, + dataInServer: MultiStatus + ): UploadedChunks { + val uploadPath = requireNotNull(client.uploadUri.path) + var nextByte = 0L + var lastId = 0 + + for (response in dataInServer.responses) { + val entry = WebdavEntry(response, uploadPath) + val chunkId = entry.chunkId() ?: continue + + lastId = max(lastId, chunkId) + nextByte += entry.contentLength + } + + return UploadedChunks(nextByte, lastId) + } + + /** Id of the chunk this entry holds, or `null` for any object not matching the expected chunk name. */ + private fun WebdavEntry.chunkId(): Int? = + name + ?.takeIf { !isDirectory && it.length <= CHUNK_NAME_LENGTH && it.isDigitsOnly() } + ?.toIntOrNull() + + private fun uploadRemainingChunks( + client: OwnCloudClient, + file: File, + uploaded: UploadedChunks + ): RemoteOperationResult? = + remainingChunks(file.length(), uploaded).firstNotNullOfOrNull { chunk -> + uploadChunk(client, chunk).failureOrNull() + } + + private fun remainingChunks( + fileLength: Long, + uploaded: UploadedChunks + ): Sequence { + val chunkSize = chunkSize(onWifiConnection, serverMaxChunkSize) + val firstId = uploaded.lastId + 1 + + // everything below uploaded.nextByte is already on the server, and a single trailing byte is not chunked + return (uploaded.nextByte until fileLength - 1 step chunkSize) + .asSequence() + .mapIndexed { index, startByte -> calcNextChunk(fileLength, firstId + index, startByte, chunkSize) } + } + + /** The result to report, or `null` when the chunk went through and the upload may continue. */ + private fun RemoteOperationResult.failureOrNull(): RemoteOperationResult? = + when { + !isSuccess -> this + cancellationRequested.get() -> RemoteOperationResult(OperationCancelledException()) + else -> null + } + + private fun assemble( + client: OwnCloudClient, + file: File + ): RemoteOperationResult { + val move = MoveMethod(uploadFolderUri + ASSEMBLED_FILE_SUFFIX, destinationUri, true) + moveMethod = move + + move.addRequestHeader(OC_X_OC_MTIME_HEADER, lastModificationTimestamp.toString()) + creationTimestamp?.takeIf { it > 0 }?.let { move.addRequestHeader(OC_X_OC_CTIME_HEADER, it.toString()) } + token?.let { move.addRequestHeader(E2E_TOKEN, it) } + + val status = client.executeMethod(move, calculateAssembleTimeout(file), DO_NOT_CHANGE_DEFAULT) + + return RemoteOperationResult(isSuccess(status), move) + } + + @Throws(IOException::class) + private fun uploadChunk( + client: OwnCloudClient, + chunk: Chunk + ): RemoteOperationResult { + val file = File(localPath) + var raf: RandomAccessFile? = null + var channel: FileChannel? = null + + return try { + raf = RandomAccessFile(file, "r") + channel = raf.channel + entity = ChunkFromFileChannelRequestEntity(channel, mimeType, chunk.start, chunk.length, file) + + synchronized(dataTransferListeners) { + (entity as ProgressiveDataTransfer).addDataTransferProgressListeners(dataTransferListeners) + } + + // pad chunk name to 6 digits + val chunkName = String.format(Locale.ROOT, "%0${CHUNK_NAME_LENGTH}d", chunk.id) + + putMethod?.releaseConnection() // let the connection available for other methods + + val put = createPutMethod("$uploadFolderUri/$chunkName") + put.addRequestHeader(DESTINATION_HEADER, destinationUri) + token?.let { put.addRequestHeader(E2E_TOKEN, it) } + + val status = client.executeMethod(put) + val result = RemoteOperationResult(isSuccess(status), put) + + client.exhaustResponse(put.responseBodyAsStream) + Log_OC.d( + TAG, + "Upload of $localPath to $remotePath, chunk id: ${chunk.id} from ${chunk.start} " + + "size: ${chunk.length}, HTTP result status $status" + ) + + result + } finally { + channel.closeQuietly("Error closing file channel!") + raf.closeQuietly("Error closing file access!") + putMethod?.releaseConnection() + } + } + + private fun createPutMethod(uri: String): PutMethod { + val put = PutMethod(uri) + putMethod = put + put.requestEntity = entity + + if (cancellationRequested.get()) { + put.abort() // next method will throw an exception + } + + return put + } + + private fun cancelledOrFailed(e: Exception): RemoteOperationResult = + when { + putMethod?.isAborted == true || moveMethod?.isAborted == true -> { + cancellationReason + ?.takeIf { cancellationRequested.get() } + ?.let { RemoteOperationResult(it) } + ?: RemoteOperationResult(OperationCancelledException()) + } + + else -> { + RemoteOperationResult(e) + } + } + + private fun Closeable?.closeQuietly(errorMessage: String) { + try { + this?.close() + } catch (e: IOException) { + Log_OC.e(TAG, errorMessage, e) + } + } + + @VisibleForTesting + fun calculateAssembleTimeout(file: File): Int { + val fileSizeInGb = file.length() / BYTES_PER_GB + + return max(assembleTimeMin, min((assembleTimePerGB * fileSizeInGb).toInt(), assembleTimeMax)) + } + + private data class UploadedChunks( + val nextByte: Long, + val lastId: Int + ) + + companion object { + const val MIN_CHUNK_SIZE: Long = 10240000 + const val DEFAULT_CHUNK_SIZE: Long = 40960000 + const val SERVER_MAX_CHUNK_SIZE_UNKNOWN: Long = -1 + const val DESTINATION_HEADER: String = "Destination" + const val CHUNK_NAME_LENGTH: Int = 6 + + @JvmStatic + fun chunkSize( + onWifiConnection: Boolean, + serverMaxChunkSize: Long + ): Long { + if (serverMaxChunkSize <= 0) { + return if (onWifiConnection) DEFAULT_CHUNK_SIZE else MIN_CHUNK_SIZE + } + + return serverMaxChunkSize + } + + private const val ASSEMBLED_FILE_SUFFIX = "/.file" + private const val CREATE_FOLDER_READ_TIMEOUT = 30_000 + private const val DO_NOT_CHANGE_DEFAULT = -1 + private const val BYTES_PER_GB = 1e9 + private val TAG = ChunkedFileUploadRemoteOperation::class.java.simpleName + + internal fun calcNextChunk( + fileSize: Long, + chunkId: Int, + startByte: Long, + chunkSize: Long + ): Chunk { + require(chunkId >= 0 && chunkId.toString().length <= CHUNK_NAME_LENGTH) { + "chunkId must not exceed length specified in CHUNK_NAME_LENGTH ($CHUNK_NAME_LENGTH)" + } + + val length = if (startByte + chunkSize > fileSize) fileSize - startByte else chunkSize + + return Chunk(chunkId, startByte, length) + } + } + } diff --git a/library/src/main/java/com/owncloud/android/lib/resources/status/GetCapabilitiesRemoteOperation.java b/library/src/main/java/com/owncloud/android/lib/resources/status/GetCapabilitiesRemoteOperation.java index d1a96fce37..b6ecb690c6 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/status/GetCapabilitiesRemoteOperation.java +++ b/library/src/main/java/com/owncloud/android/lib/resources/status/GetCapabilitiesRemoteOperation.java @@ -94,6 +94,8 @@ public class GetCapabilitiesRemoteOperation extends RemoteOperation - checkChunks(length, ChunkedFileUploadRemoteOperation.CHUNK_SIZE_MOBILE, 0) - checkChunks(length, ChunkedFileUploadRemoteOperation.CHUNK_SIZE_WIFI, 0) + checkChunks(length, ChunkedFileUploadRemoteOperation.MIN_CHUNK_SIZE, 0) + checkChunks(length, ChunkedFileUploadRemoteOperation.DEFAULT_CHUNK_SIZE, 0) } } @@ -102,8 +102,8 @@ class ChunkedFileUploadRemoteOperationTest { // test chunking with offset (chunks already on server) // -2: last byte missing (because the file starts at 0B, file.length() at 1; 1B offset) listOf(1, 1 * MB, 10 * MB, 100 * MB, 256 * MB, 1 * GB - 2).forEach { offset -> - checkChunks(1 * GB, ChunkedFileUploadRemoteOperation.CHUNK_SIZE_MOBILE, offset) - checkChunks(1 * GB, ChunkedFileUploadRemoteOperation.CHUNK_SIZE_WIFI, offset) + checkChunks(1 * GB, ChunkedFileUploadRemoteOperation.MIN_CHUNK_SIZE, offset) + checkChunks(1 * GB, ChunkedFileUploadRemoteOperation.DEFAULT_CHUNK_SIZE, offset) } } @@ -121,7 +121,7 @@ class ChunkedFileUploadRemoteOperationTest { length, ++id, nextByte, - ChunkedFileUploadRemoteOperation.CHUNK_SIZE_WIFI + ChunkedFileUploadRemoteOperation.DEFAULT_CHUNK_SIZE ) chunks.add(chunk) @@ -135,7 +135,7 @@ class ChunkedFileUploadRemoteOperationTest { length, ++id, nextByte, - ChunkedFileUploadRemoteOperation.CHUNK_SIZE_MOBILE + ChunkedFileUploadRemoteOperation.MIN_CHUNK_SIZE ) chunks.add(chunk) @@ -144,11 +144,11 @@ class ChunkedFileUploadRemoteOperationTest { // calculate expected number of chunks var expectedChunkCount = - ceil((length / 2) / ChunkedFileUploadRemoteOperation.CHUNK_SIZE_WIFI.toFloat()) + ceil((length / 2) / ChunkedFileUploadRemoteOperation.DEFAULT_CHUNK_SIZE.toFloat()) expectedChunkCount += ceil( - (length - expectedChunkCount * ChunkedFileUploadRemoteOperation.CHUNK_SIZE_WIFI) / - ChunkedFileUploadRemoteOperation.CHUNK_SIZE_MOBILE.toFloat() + (length - expectedChunkCount * ChunkedFileUploadRemoteOperation.DEFAULT_CHUNK_SIZE) / + ChunkedFileUploadRemoteOperation.MIN_CHUNK_SIZE.toFloat() ) assertEquals(expectedChunkCount.toInt(), chunks.size)