1
0
mirror of https://github.com/owncloud/android-library.git synced 2025-06-07 16:06:08 +00:00

Basic upload with chunks [WIP]

This commit is contained in:
davigonz 2018-06-25 15:32:00 +02:00
parent 366459699e
commit 153c0a10ff
4 changed files with 184 additions and 73 deletions

View File

@ -56,7 +56,7 @@ public class ChunkFromFileChannelRequestEntity implements RequestEntity, Progres
private final File mFile;
private long mOffset;
private long mTransferred;
Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<OnDatatransferProgressListener>();
Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<>();
private ByteBuffer mBuffer = ByteBuffer.allocate(4096);
public ChunkFromFileChannelRequestEntity(
@ -120,8 +120,8 @@ public class ChunkFromFileChannelRequestEntity implements RequestEntity, Progres
public void writeRequest(final OutputStream out) throws IOException {
int readCount = 0;
Iterator<OnDatatransferProgressListener> it = null;
int readCount;
Iterator<OnDatatransferProgressListener> it;
try {
mChannel.position(mOffset);
@ -161,7 +161,5 @@ public class ChunkFromFileChannelRequestEntity implements RequestEntity, Progres
} catch (FileRequestEntity.WriteException we) {
throw we.getWrapped();
}
}
}

View File

@ -0,0 +1,100 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2018 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package com.owncloud.android.lib.common.network;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Iterator;
import okhttp3.MediaType;
import okio.BufferedSink;
public class ChunkFromFileRequestBody extends FileRequestBody {
private static final String TAG = ChunkFromFileChannelRequestEntity.class.getSimpleName();
//private final File mFile;
private final FileChannel mChannel;
private final long mChunkSize;
private long mOffset;
private long mTransferred;
private ByteBuffer mBuffer = ByteBuffer.allocate(4096);
public ChunkFromFileRequestBody(File file, MediaType contentType, FileChannel channel, long chunkSize) {
super(file, contentType);
if (channel == null) {
throw new IllegalArgumentException("File may not be null");
}
if (chunkSize <= 0) {
throw new IllegalArgumentException("Chunk size must be greater than zero");
}
this.mChannel = channel;
this.mChunkSize = chunkSize;
mOffset = 0;
mTransferred = 0;
}
@Override
public void writeTo(BufferedSink sink) {
int readCount;
Iterator<OnDatatransferProgressListener> it;
try {
mChannel.position(mOffset);
long size = mFile.length();
if (size == 0) size = -1;
long maxCount = Math.min(mOffset + mChunkSize, mChannel.size());
while (mChannel.position() < maxCount) {
readCount = mChannel.read(mBuffer);
sink.write(mBuffer.array(), 0 ,readCount);
mBuffer.clear();
if (mTransferred < maxCount) { // condition to avoid accumulate progress for repeated chunks
mTransferred += readCount;
}
synchronized (mDataTransferListeners) {
it = mDataTransferListeners.iterator();
while (it.hasNext()) {
it.next().onTransferProgress(readCount, mTransferred, size, mFile.getAbsolutePath());
}
}
}
sink.flush();
} catch (IOException io) {
// // any read problem will be handled as if the file is not there
// if (io instanceof FileNotFoundException) {
// throw io;
// } else {
// FileNotFoundException fnf = new FileNotFoundException("Exception reading source file");
// fnf.initCause(io);
// throw fnf;
// }
}
}
}

View File

@ -43,7 +43,7 @@ import okio.Source;
*/
public class FileRequestBody extends RequestBody implements ProgressiveDataTransferer {
private File mFile;
protected File mFile;
private MediaType mContentType;
Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<>();
@ -71,7 +71,7 @@ public class FileRequestBody extends RequestBody implements ProgressiveDataTrans
long transferred = 0;
long read;
while ((read = source.read(sink.buffer(), 2048)) != -1) {
while ((read = source.read(sink.buffer(), 4096)) != -1) {
transferred += read;
sink.flush();
synchronized (mDataTransferListeners) {

View File

@ -1,5 +1,5 @@
/* ownCloud Android Library is available under MIT license
* Copyright (C) 2016 ownCloud GmbH.
* Copyright (C) 2018 ownCloud GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -26,14 +26,28 @@ package com.owncloud.android.lib.resources.files;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.http.HttpUtils;
import com.owncloud.android.lib.common.http.methods.webdav.MkColMethod;
import com.owncloud.android.lib.common.network.WebdavUtils;
import com.owncloud.android.lib.common.http.methods.webdav.PutMethod;
import com.owncloud.android.lib.common.network.ChunkFromFileRequestBody;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.lib.common.utils.Log_OC;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import okhttp3.MediaType;
import static com.owncloud.android.lib.common.http.HttpConstants.IF_MATCH_HEADER;
import static com.owncloud.android.lib.common.http.HttpConstants.OC_TOTAL_LENGTH_HEADER;
import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK;
/**
* Remote operation performing the chunked upload of a remote file to the ownCloud server.
*
* @author David A. Velasco
* @author David González Verdugo
*/
public class ChunkedUploadRemoteFileOperation extends UploadRemoteFileOperation {
private static final int LAST_CHUNK_TIMEOUT = 900000; //15 mins.
@ -46,11 +60,6 @@ public class ChunkedUploadRemoteFileOperation extends UploadRemoteFileOperation
private long mTransferId;
public ChunkedUploadRemoteFileOperation(String storagePath, String remotePath, String mimeType,
String fileLastModifTimestamp) {
super(storagePath, remotePath, mimeType, fileLastModifTimestamp);
}
public ChunkedUploadRemoteFileOperation(long transferId, String storagePath, String remotePath, String mimeType,
String requiredEtag, String fileLastModifTimestamp) {
super(storagePath, remotePath, mimeType, requiredEtag, fileLastModifTimestamp);
@ -65,80 +74,84 @@ public class ChunkedUploadRemoteFileOperation extends UploadRemoteFileOperation
FileChannel channel = null;
RandomAccessFile raf = null;
//TODO
// try {
// File file = new File(mLocalPath);
// raf = new RandomAccessFile(file, "r");
// channel = raf.getChannel();
// mEntity = new ChunkFromFileChannelRequestEntity(channel, mMimeType, CHUNK_SIZE, file);
// synchronized (mDataTransferListeners) {
// ((ProgressiveDataTransferer) mEntity)
// .addDatatransferProgressListeners(mDataTransferListeners);
// }
//
// long offset = 0;
// String uriPrefix = client.getOldWebdavUri() + WebdavUtils.encodePath(mRemotePath) +
// "-chunking-" + Math.abs((new Random()).nextInt(9000) + 1000) + "-";
// long totalLength = file.length();
// long chunkCount = (long) Math.ceil((double) totalLength / CHUNK_SIZE);
// String chunkSizeStr = String.valueOf(CHUNK_SIZE);
// String totalLengthStr = String.valueOf(file.length());
// for (int chunkIndex = 0; chunkIndex < chunkCount; chunkIndex++, offset += CHUNK_SIZE) {
// if (chunkIndex == chunkCount - 1) {
// chunkSizeStr = String.valueOf(CHUNK_SIZE * chunkCount - totalLength);
// }
// if (mPutMethod != null) {
// mPutMethod.releaseConnection(); // let the connection available
// // for other methods
// }
// mPutMethod = new PutMethod(uriPrefix + chunkCount + "-" + chunkIndex);
// if (mRequiredEtag != null && mRequiredEtag.length() > 0) {
// mPutMethod.addRequestHeader(IF_MATCH_HEADER, "\"" + mRequiredEtag + "\"");
// }
try {
File fileToUpload = new File(mLocalPath);
MediaType mediaType = MediaType.parse(mMimeType);
raf = new RandomAccessFile(fileToUpload, "r");
channel = raf.getChannel();
mFileRequestBody = new ChunkFromFileRequestBody(fileToUpload, mediaType, channel, CHUNK_SIZE);
synchronized (mDataTransferListeners) {
mFileRequestBody.addDatatransferProgressListeners(mDataTransferListeners);
}
long offset = 0;
String uriPrefix = client.getNewUploadsWebDavUri() + FileUtils.PATH_SEPARATOR + String.valueOf(mTransferId);
long totalLength = fileToUpload.length();
long chunkCount = (long) Math.ceil((double) totalLength / CHUNK_SIZE);
String chunkSizeStr = String.valueOf(CHUNK_SIZE);
String totalLengthStr = String.valueOf(fileToUpload.length());
for (int chunkIndex = 0; chunkIndex < chunkCount; chunkIndex++, offset += CHUNK_SIZE) {
if (chunkIndex == chunkCount - 1) {
chunkSizeStr = String.valueOf(CHUNK_SIZE * chunkCount - totalLength);
}
mPutMethod = new PutMethod(
HttpUtils.stringUrlToHttpUrl(uriPrefix + FileUtils.PATH_SEPARATOR + chunkIndex)
);
if (mRequiredEtag != null && mRequiredEtag.length() > 0) {
mPutMethod.addRequestHeader(IF_MATCH_HEADER, "\"" + mRequiredEtag + "\"");
}
// mPutMethod.addRequestHeader(OC_CHUNKED_HEADER, OC_CHUNKED_HEADER);
// mPutMethod.addRequestHeader(OC_CHUNK_SIZE_HEADER, chunkSizeStr);
// mPutMethod.addRequestHeader(OC_TOTAL_LENGTH_HEADER, totalLengthStr);
//
// mPutMethod.addRequestHeader(OC_CHUNK_X_OC_MTIME_HEADER, mFileLastModifTimestamp);
//
mPutMethod.addRequestHeader(OC_TOTAL_LENGTH_HEADER, totalLengthStr);
mPutMethod.addRequestHeader(OC_CHUNK_X_OC_MTIME_HEADER, mFileLastModifTimestamp);
// ((ChunkFromFileChannelRequestEntity) mEntity).setOffset(offset);
// mPutMethod.setRequestEntity(mEntity);
// if (mCancellationRequested.get()) {
// mPutMethod.abort();
// // next method will throw an exception
// }
//
// if (chunkIndex == chunkCount - 1) {
// // Added a high timeout to the last chunk due to when the last chunk
// // arrives to the server with the last PUT, all chunks get assembled
// // within that PHP request, so last one takes longer.
// mPutMethod.getParams().setSoTimeout(LAST_CHUNK_TIMEOUT);
// }
//
// status = client.executeMethod(mPutMethod);
//
// result = new RemoteOperationResult(
// isSuccess(status),
// mPutMethod
// );
//
// client.exhaustResponse(mPutMethod.getResponseBodyAsStream());
// Log_OC.d(TAG, "Upload of " + mLocalPath + " to " + mRemotePath +
// ", chunk index " + chunkIndex + ", count " + chunkCount +
// ", HTTP result status " + status);
//
// if (!isSuccess(status))
// break;
// }
//
// } finally {
// if (channel != null)
// channel.close();
// if (raf != null)
// raf.close();
mPutMethod.setRequestBody(mFileRequestBody);
status = client.executeHttpMethod(mPutMethod);
Log_OC.d(TAG, "Upload of " + mLocalPath + " to " + mRemotePath +
", chunk index " + chunkIndex + ", count " + chunkCount +
", HTTP result status " + status);
if (isSuccess(status)) {
result = new RemoteOperationResult(OK);
} else {
result = new RemoteOperationResult(mPutMethod);
break;
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (channel != null)
channel.close();
if (raf != null)
raf.close();
// if (mPutMethod != null)
// mPutMethod.releaseConnection(); // let the connection available for other methods
// }
}
return result;
}
}