mirror of
https://github.com/owncloud/android-library.git
synced 2025-06-08 00:16:09 +00:00
Basic upload with chunks [WIP]
This commit is contained in:
parent
366459699e
commit
153c0a10ff
@ -56,7 +56,7 @@ public class ChunkFromFileChannelRequestEntity implements RequestEntity, Progres
|
|||||||
private final File mFile;
|
private final File mFile;
|
||||||
private long mOffset;
|
private long mOffset;
|
||||||
private long mTransferred;
|
private long mTransferred;
|
||||||
Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<OnDatatransferProgressListener>();
|
Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<>();
|
||||||
private ByteBuffer mBuffer = ByteBuffer.allocate(4096);
|
private ByteBuffer mBuffer = ByteBuffer.allocate(4096);
|
||||||
|
|
||||||
public ChunkFromFileChannelRequestEntity(
|
public ChunkFromFileChannelRequestEntity(
|
||||||
@ -120,8 +120,8 @@ public class ChunkFromFileChannelRequestEntity implements RequestEntity, Progres
|
|||||||
|
|
||||||
|
|
||||||
public void writeRequest(final OutputStream out) throws IOException {
|
public void writeRequest(final OutputStream out) throws IOException {
|
||||||
int readCount = 0;
|
int readCount;
|
||||||
Iterator<OnDatatransferProgressListener> it = null;
|
Iterator<OnDatatransferProgressListener> it;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mChannel.position(mOffset);
|
mChannel.position(mOffset);
|
||||||
@ -161,7 +161,5 @@ public class ChunkFromFileChannelRequestEntity implements RequestEntity, Progres
|
|||||||
} catch (FileRequestEntity.WriteException we) {
|
} catch (FileRequestEntity.WriteException we) {
|
||||||
throw we.getWrapped();
|
throw we.getWrapped();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -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;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -43,7 +43,7 @@ import okio.Source;
|
|||||||
*/
|
*/
|
||||||
public class FileRequestBody extends RequestBody implements ProgressiveDataTransferer {
|
public class FileRequestBody extends RequestBody implements ProgressiveDataTransferer {
|
||||||
|
|
||||||
private File mFile;
|
protected File mFile;
|
||||||
private MediaType mContentType;
|
private MediaType mContentType;
|
||||||
Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<>();
|
Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<>();
|
||||||
|
|
||||||
@ -71,7 +71,7 @@ public class FileRequestBody extends RequestBody implements ProgressiveDataTrans
|
|||||||
long transferred = 0;
|
long transferred = 0;
|
||||||
long read;
|
long read;
|
||||||
|
|
||||||
while ((read = source.read(sink.buffer(), 2048)) != -1) {
|
while ((read = source.read(sink.buffer(), 4096)) != -1) {
|
||||||
transferred += read;
|
transferred += read;
|
||||||
sink.flush();
|
sink.flush();
|
||||||
synchronized (mDataTransferListeners) {
|
synchronized (mDataTransferListeners) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* ownCloud Android Library is available under MIT license
|
/* 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
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* 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.OwnCloudClient;
|
||||||
import com.owncloud.android.lib.common.http.HttpUtils;
|
import com.owncloud.android.lib.common.http.HttpUtils;
|
||||||
import com.owncloud.android.lib.common.http.methods.webdav.MkColMethod;
|
import com.owncloud.android.lib.common.http.methods.webdav.PutMethod;
|
||||||
import com.owncloud.android.lib.common.network.WebdavUtils;
|
import com.owncloud.android.lib.common.network.ChunkFromFileRequestBody;
|
||||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
|
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.IOException;
|
||||||
import java.io.RandomAccessFile;
|
import java.io.RandomAccessFile;
|
||||||
import java.nio.channels.FileChannel;
|
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 {
|
public class ChunkedUploadRemoteFileOperation extends UploadRemoteFileOperation {
|
||||||
|
|
||||||
private static final int LAST_CHUNK_TIMEOUT = 900000; //15 mins.
|
private static final int LAST_CHUNK_TIMEOUT = 900000; //15 mins.
|
||||||
@ -46,11 +60,6 @@ public class ChunkedUploadRemoteFileOperation extends UploadRemoteFileOperation
|
|||||||
|
|
||||||
private long mTransferId;
|
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,
|
public ChunkedUploadRemoteFileOperation(long transferId, String storagePath, String remotePath, String mimeType,
|
||||||
String requiredEtag, String fileLastModifTimestamp) {
|
String requiredEtag, String fileLastModifTimestamp) {
|
||||||
super(storagePath, remotePath, mimeType, requiredEtag, fileLastModifTimestamp);
|
super(storagePath, remotePath, mimeType, requiredEtag, fileLastModifTimestamp);
|
||||||
@ -65,80 +74,84 @@ public class ChunkedUploadRemoteFileOperation extends UploadRemoteFileOperation
|
|||||||
FileChannel channel = null;
|
FileChannel channel = null;
|
||||||
RandomAccessFile raf = null;
|
RandomAccessFile raf = null;
|
||||||
|
|
||||||
//TODO
|
try {
|
||||||
// try {
|
File fileToUpload = new File(mLocalPath);
|
||||||
// File file = new File(mLocalPath);
|
MediaType mediaType = MediaType.parse(mMimeType);
|
||||||
// raf = new RandomAccessFile(file, "r");
|
|
||||||
// channel = raf.getChannel();
|
raf = new RandomAccessFile(fileToUpload, "r");
|
||||||
// mEntity = new ChunkFromFileChannelRequestEntity(channel, mMimeType, CHUNK_SIZE, file);
|
channel = raf.getChannel();
|
||||||
// synchronized (mDataTransferListeners) {
|
|
||||||
// ((ProgressiveDataTransferer) mEntity)
|
mFileRequestBody = new ChunkFromFileRequestBody(fileToUpload, mediaType, channel, CHUNK_SIZE);
|
||||||
// .addDatatransferProgressListeners(mDataTransferListeners);
|
|
||||||
// }
|
synchronized (mDataTransferListeners) {
|
||||||
//
|
mFileRequestBody.addDatatransferProgressListeners(mDataTransferListeners);
|
||||||
// long offset = 0;
|
}
|
||||||
// String uriPrefix = client.getOldWebdavUri() + WebdavUtils.encodePath(mRemotePath) +
|
|
||||||
// "-chunking-" + Math.abs((new Random()).nextInt(9000) + 1000) + "-";
|
long offset = 0;
|
||||||
// long totalLength = file.length();
|
String uriPrefix = client.getNewUploadsWebDavUri() + FileUtils.PATH_SEPARATOR + String.valueOf(mTransferId);
|
||||||
// long chunkCount = (long) Math.ceil((double) totalLength / CHUNK_SIZE);
|
long totalLength = fileToUpload.length();
|
||||||
// String chunkSizeStr = String.valueOf(CHUNK_SIZE);
|
long chunkCount = (long) Math.ceil((double) totalLength / CHUNK_SIZE);
|
||||||
// String totalLengthStr = String.valueOf(file.length());
|
String chunkSizeStr = String.valueOf(CHUNK_SIZE);
|
||||||
// for (int chunkIndex = 0; chunkIndex < chunkCount; chunkIndex++, offset += CHUNK_SIZE) {
|
String totalLengthStr = String.valueOf(fileToUpload.length());
|
||||||
// if (chunkIndex == chunkCount - 1) {
|
|
||||||
// chunkSizeStr = String.valueOf(CHUNK_SIZE * chunkCount - totalLength);
|
for (int chunkIndex = 0; chunkIndex < chunkCount; chunkIndex++, offset += CHUNK_SIZE) {
|
||||||
// }
|
if (chunkIndex == chunkCount - 1) {
|
||||||
// if (mPutMethod != null) {
|
chunkSizeStr = String.valueOf(CHUNK_SIZE * chunkCount - totalLength);
|
||||||
// mPutMethod.releaseConnection(); // let the connection available
|
}
|
||||||
// // for other methods
|
|
||||||
// }
|
mPutMethod = new PutMethod(
|
||||||
// mPutMethod = new PutMethod(uriPrefix + chunkCount + "-" + chunkIndex);
|
HttpUtils.stringUrlToHttpUrl(uriPrefix + FileUtils.PATH_SEPARATOR + chunkIndex)
|
||||||
// if (mRequiredEtag != null && mRequiredEtag.length() > 0) {
|
);
|
||||||
// mPutMethod.addRequestHeader(IF_MATCH_HEADER, "\"" + mRequiredEtag + "\"");
|
|
||||||
// }
|
if (mRequiredEtag != null && mRequiredEtag.length() > 0) {
|
||||||
|
mPutMethod.addRequestHeader(IF_MATCH_HEADER, "\"" + mRequiredEtag + "\"");
|
||||||
|
}
|
||||||
// mPutMethod.addRequestHeader(OC_CHUNKED_HEADER, OC_CHUNKED_HEADER);
|
// mPutMethod.addRequestHeader(OC_CHUNKED_HEADER, OC_CHUNKED_HEADER);
|
||||||
// mPutMethod.addRequestHeader(OC_CHUNK_SIZE_HEADER, chunkSizeStr);
|
// mPutMethod.addRequestHeader(OC_CHUNK_SIZE_HEADER, chunkSizeStr);
|
||||||
// mPutMethod.addRequestHeader(OC_TOTAL_LENGTH_HEADER, totalLengthStr);
|
mPutMethod.addRequestHeader(OC_TOTAL_LENGTH_HEADER, totalLengthStr);
|
||||||
//
|
|
||||||
// mPutMethod.addRequestHeader(OC_CHUNK_X_OC_MTIME_HEADER, mFileLastModifTimestamp);
|
mPutMethod.addRequestHeader(OC_CHUNK_X_OC_MTIME_HEADER, mFileLastModifTimestamp);
|
||||||
//
|
|
||||||
// ((ChunkFromFileChannelRequestEntity) mEntity).setOffset(offset);
|
// ((ChunkFromFileChannelRequestEntity) mEntity).setOffset(offset);
|
||||||
// mPutMethod.setRequestEntity(mEntity);
|
// mPutMethod.setRequestEntity(mEntity);
|
||||||
// if (mCancellationRequested.get()) {
|
// if (mCancellationRequested.get()) {
|
||||||
// mPutMethod.abort();
|
// mPutMethod.abort();
|
||||||
// // next method will throw an exception
|
// // next method will throw an exception
|
||||||
// }
|
// }
|
||||||
//
|
|
||||||
// if (chunkIndex == chunkCount - 1) {
|
// if (chunkIndex == chunkCount - 1) {
|
||||||
// // Added a high timeout to the last chunk due to when the last chunk
|
// // 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
|
// // arrives to the server with the last PUT, all chunks get assembled
|
||||||
// // within that PHP request, so last one takes longer.
|
// // within that PHP request, so last one takes longer.
|
||||||
// mPutMethod.getParams().setSoTimeout(LAST_CHUNK_TIMEOUT);
|
// mPutMethod.getParams().setSoTimeout(LAST_CHUNK_TIMEOUT);
|
||||||
// }
|
// }
|
||||||
//
|
|
||||||
// status = client.executeMethod(mPutMethod);
|
mPutMethod.setRequestBody(mFileRequestBody);
|
||||||
//
|
|
||||||
// result = new RemoteOperationResult(
|
status = client.executeHttpMethod(mPutMethod);
|
||||||
// isSuccess(status),
|
|
||||||
// mPutMethod
|
Log_OC.d(TAG, "Upload of " + mLocalPath + " to " + mRemotePath +
|
||||||
// );
|
", chunk index " + chunkIndex + ", count " + chunkCount +
|
||||||
//
|
", HTTP result status " + status);
|
||||||
// client.exhaustResponse(mPutMethod.getResponseBodyAsStream());
|
|
||||||
// Log_OC.d(TAG, "Upload of " + mLocalPath + " to " + mRemotePath +
|
if (isSuccess(status)) {
|
||||||
// ", chunk index " + chunkIndex + ", count " + chunkCount +
|
result = new RemoteOperationResult(OK);
|
||||||
// ", HTTP result status " + status);
|
} else {
|
||||||
//
|
result = new RemoteOperationResult(mPutMethod);
|
||||||
// if (!isSuccess(status))
|
break;
|
||||||
// break;
|
}
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// } finally {
|
} catch (Exception e) {
|
||||||
// if (channel != null)
|
e.printStackTrace();
|
||||||
// channel.close();
|
} finally {
|
||||||
// if (raf != null)
|
if (channel != null)
|
||||||
// raf.close();
|
channel.close();
|
||||||
|
if (raf != null)
|
||||||
|
raf.close();
|
||||||
// if (mPutMethod != null)
|
// if (mPutMethod != null)
|
||||||
// mPutMethod.releaseConnection(); // let the connection available for other methods
|
// mPutMethod.releaseConnection(); // let the connection available for other methods
|
||||||
// }
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user