X-Git-Url: http://git.linex4red.de/pub/Android/ownCloud.git/blobdiff_plain/b0ab3ce0872d2702e82b5979dddaa5a897be340b..e1245bbda7894e1a3028d2b54a6918e4bdd46ae2:/src/com/owncloud/android/operations/UploadFileOperation.java?ds=inline diff --git a/src/com/owncloud/android/operations/UploadFileOperation.java b/src/com/owncloud/android/operations/UploadFileOperation.java index e005f179..80a3463b 100644 --- a/src/com/owncloud/android/operations/UploadFileOperation.java +++ b/src/com/owncloud/android/operations/UploadFileOperation.java @@ -1,10 +1,9 @@ /* ownCloud Android client application - * Copyright (C) 2012 Bartek Przybylski + * Copyright (C) 2012-2013 ownCloud Inc. * * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -19,24 +18,36 @@ package com.owncloud.android.operations; import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.util.HashSet; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; import org.apache.commons.httpclient.HttpException; import org.apache.commons.httpclient.methods.PutMethod; +import org.apache.commons.httpclient.methods.RequestEntity; import org.apache.http.HttpStatus; -import com.owncloud.android.operations.RemoteOperation; -import com.owncloud.android.operations.RemoteOperationResult; +import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.files.services.FileUploader; +import com.owncloud.android.oc_framework.network.ProgressiveDataTransferer; +import com.owncloud.android.oc_framework.network.webdav.FileRequestEntity; +import com.owncloud.android.oc_framework.network.webdav.OnDatatransferProgressListener; +import com.owncloud.android.oc_framework.network.webdav.WebdavClient; +import com.owncloud.android.oc_framework.network.webdav.WebdavUtils; +import com.owncloud.android.oc_framework.operations.OperationCancelledException; +import com.owncloud.android.oc_framework.operations.RemoteOperation; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode; +import com.owncloud.android.utils.FileStorageUtils; +import com.owncloud.android.utils.Log_OC; -import eu.alefzero.webdav.FileRequestEntity; -import eu.alefzero.webdav.OnDatatransferProgressListener; -import eu.alefzero.webdav.WebdavClient; -import eu.alefzero.webdav.WebdavUtils; import android.accounts.Account; -import android.util.Log; -import android.webkit.MimeTypeMap; + /** * Remote operation performing the upload of a file to an ownCloud server @@ -44,138 +55,335 @@ import android.webkit.MimeTypeMap; * @author David A. Velasco */ public class UploadFileOperation extends RemoteOperation { - - private static final String TAG = UploadFileOperation.class.getCanonicalName(); - private Account mAccount = null; - private String mLocalPath = null; + private static final String TAG = UploadFileOperation.class.getSimpleName(); + + private Account mAccount; + private OCFile mFile; + private OCFile mOldFile; private String mRemotePath = null; - private String mMimeType = null; private boolean mIsInstant = false; + private boolean mRemoteFolderToBeCreated = false; private boolean mForceOverwrite = false; + private int mLocalBehaviour = FileUploader.LOCAL_BEHAVIOUR_COPY; + private boolean mWasRenamed = false; + private String mOriginalFileName = null; + private String mOriginalStoragePath = null; + PutMethod mPutMethod = null; private Set mDataTransferListeners = new HashSet(); + private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false); + + protected RequestEntity mEntity = null; public UploadFileOperation( Account account, - String localPath, - String remotePath, - String mimeType, + OCFile file, boolean isInstant, - boolean forceOverwrite) { + boolean forceOverwrite, + int localBehaviour) { if (account == null) - throw new IllegalArgumentException("Illegal null account in UploadFileOperation creation"); - if (localPath == null || localPath.length() <= 0) - throw new IllegalArgumentException("Illegal null or empty localPath in UploadFileOperation creation"); - if (remotePath == null || remotePath.length() <= 0) - throw new IllegalArgumentException("Illegal null or empty remotePath in UploadFileOperation creation"); + throw new IllegalArgumentException("Illegal NULL account in UploadFileOperation creation"); + if (file == null) + throw new IllegalArgumentException("Illegal NULL file in UploadFileOperation creation"); + if (file.getStoragePath() == null || file.getStoragePath().length() <= 0 + || !(new File(file.getStoragePath()).exists())) { + throw new IllegalArgumentException( + "Illegal file in UploadFileOperation; storage path invalid or file not found: " + + file.getStoragePath()); + } mAccount = account; - mLocalPath = localPath; - mRemotePath = remotePath; - mMimeType = mimeType; - if (mMimeType == null || mMimeType.length() <= 0) { - try { - mMimeType = MimeTypeMap.getSingleton() - .getMimeTypeFromExtension( - localPath.substring(localPath.lastIndexOf('.') + 1)); - } catch (IndexOutOfBoundsException e) { - Log.e(TAG, "Trying to find out MIME type of a file without extension: " + localPath); - } - } - if (mMimeType == null) { - mMimeType = "application/octet-stream"; - } + mFile = file; + mRemotePath = file.getRemotePath(); mIsInstant = isInstant; mForceOverwrite = forceOverwrite; + mLocalBehaviour = localBehaviour; + mOriginalStoragePath = mFile.getStoragePath(); + mOriginalFileName = mFile.getFileName(); } - public Account getAccount() { return mAccount; } - - public String getLocalPath() { - return mLocalPath; + + public String getFileName() { + return mOriginalFileName; + } + + public OCFile getFile() { + return mFile; + } + + public OCFile getOldFile() { + return mOldFile; + } + + public String getOriginalStoragePath() { + return mOriginalStoragePath; + } + + public String getStoragePath() { + return mFile.getStoragePath(); } public String getRemotePath() { - return mRemotePath; + return mFile.getRemotePath(); } public String getMimeType() { - return mMimeType; + return mFile.getMimetype(); } - + public boolean isInstant() { return mIsInstant; } - + + public boolean isRemoteFolderToBeCreated() { + return mRemoteFolderToBeCreated; + } + + public void setRemoteFolderToBeCreated() { + mRemoteFolderToBeCreated = true; + } + public boolean getForceOverwrite() { return mForceOverwrite; } - + public boolean wasRenamed() { + return mWasRenamed; + } + public Set getDataTransferListeners() { return mDataTransferListeners; } public void addDatatransferProgressListener (OnDatatransferProgressListener listener) { - mDataTransferListeners.add(listener); + synchronized (mDataTransferListeners) { + mDataTransferListeners.add(listener); + } + if (mEntity != null) { + ((ProgressiveDataTransferer)mEntity).addDatatransferProgressListener(listener); + } } + public void removeDatatransferProgressListener(OnDatatransferProgressListener listener) { + synchronized (mDataTransferListeners) { + mDataTransferListeners.remove(listener); + } + if (mEntity != null) { + ((ProgressiveDataTransferer)mEntity).removeDatatransferProgressListener(listener); + } + } @Override protected RemoteOperationResult run(WebdavClient client) { RemoteOperationResult result = null; - boolean nameCheckPassed = false; + boolean localCopyPassed = false, nameCheckPassed = false; + File temporalFile = null, originalFile = new File(mOriginalStoragePath), expectedFile = null; try { - /// rename the file to upload, if necessary + // / rename the file to upload, if necessary if (!mForceOverwrite) { - mRemotePath = getAvailableRemotePath(client, mRemotePath); + String remotePath = getAvailableRemotePath(client, mRemotePath); + mWasRenamed = !remotePath.equals(mRemotePath); + if (mWasRenamed) { + createNewOCFile(remotePath); + } } - - /// perform the upload nameCheckPassed = true; + + String expectedPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile); // / + // not + // before + // getAvailableRemotePath() + // !!! + expectedFile = new File(expectedPath); + + // / check location of local file; if not the expected, copy to a + // temporal file before upload (if COPY is the expected behaviour) + if (!mOriginalStoragePath.equals(expectedPath) && mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_COPY) { + + if (FileStorageUtils.getUsableSpace(mAccount.name) < originalFile.length()) { + result = new RemoteOperationResult(ResultCode.LOCAL_STORAGE_FULL); + return result; // error condition when the file should be + // copied + + } else { + String temporalPath = FileStorageUtils.getTemporalPath(mAccount.name) + mFile.getRemotePath(); + mFile.setStoragePath(temporalPath); + temporalFile = new File(temporalPath); + if (!mOriginalStoragePath.equals(temporalPath)) { // preventing + // weird + // but + // possible + // situation + InputStream in = null; + OutputStream out = null; + try { + File temporalParent = temporalFile.getParentFile(); + temporalParent.mkdirs(); + if (!temporalParent.isDirectory()) { + throw new IOException("Unexpected error: parent directory could not be created"); + } + temporalFile.createNewFile(); + if (!temporalFile.isFile()) { + throw new IOException("Unexpected error: target file could not be created"); + } + in = new FileInputStream(originalFile); + out = new FileOutputStream(temporalFile); + byte[] buf = new byte[1024]; + int len; + while ((len = in.read(buf)) > 0) { + out.write(buf, 0, len); + } + + } catch (Exception e) { + result = new RemoteOperationResult(ResultCode.LOCAL_STORAGE_NOT_COPIED); + return result; + + } finally { + try { + if (in != null) + in.close(); + } catch (Exception e) { + Log_OC.d(TAG, "Weird exception while closing input stream for " + mOriginalStoragePath + " (ignoring)", e); + } + try { + if (out != null) + out.close(); + } catch (Exception e) { + Log_OC.d(TAG, "Weird exception while closing output stream for " + expectedPath + " (ignoring)", e); + } + } + } + } + } + localCopyPassed = true; + + // / perform the upload + synchronized (mCancellationRequested) { + if (mCancellationRequested.get()) { + throw new OperationCancelledException(); + } else { + mPutMethod = new PutMethod(client.getBaseUri() + WebdavUtils.encodePath(mFile.getRemotePath())); + } + } int status = uploadFile(client); - result = new RemoteOperationResult(isSuccess(status), status); - Log.i(TAG, "Upload of " + mLocalPath + " to " + mRemotePath + ": " + result.getLogMessage()); - + + // / move local temporal file or original file to its corresponding + // location in the ownCloud local folder + if (isSuccess(status)) { + if (mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_FORGET) { + mFile.setStoragePath(null); + + } else { + mFile.setStoragePath(expectedPath); + File fileToMove = null; + if (temporalFile != null) { // FileUploader.LOCAL_BEHAVIOUR_COPY + // ; see where temporalFile was + // set + fileToMove = temporalFile; + } else { // FileUploader.LOCAL_BEHAVIOUR_MOVE + fileToMove = originalFile; + } + if (!expectedFile.equals(fileToMove)) { + File expectedFolder = expectedFile.getParentFile(); + expectedFolder.mkdirs(); + if (!expectedFolder.isDirectory() || !fileToMove.renameTo(expectedFile)) { + mFile.setStoragePath(null); // forget the local file + // by now, treat this as a success; the file was + // uploaded; the user won't like that the local file + // is not linked, but this should be a very rare + // fail; + // the best option could be show a warning message + // (but not a fail) + // result = new + // RemoteOperationResult(ResultCode.LOCAL_STORAGE_NOT_MOVED); + // return result; + } + } + } + } + + result = new RemoteOperationResult(isSuccess(status), status, (mPutMethod != null ? mPutMethod.getResponseHeaders() : null)); + } catch (Exception e) { - result = new RemoteOperationResult(e); - Log.e(TAG, "Upload of " + mLocalPath + " to " + mRemotePath + ": " + result.getLogMessage() + (nameCheckPassed?"":" (while checking file existence in server)"), e); - + // TODO something cleaner with cancellations + if (mCancellationRequested.get()) { + result = new RemoteOperationResult(new OperationCancelledException()); + } else { + result = new RemoteOperationResult(e); + } + + } finally { + if (temporalFile != null && !originalFile.equals(temporalFile)) { + temporalFile.delete(); + } + if (result.isSuccess()) { + Log_OC.i(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage()); + } else { + if (result.getException() != null) { + String complement = ""; + if (!nameCheckPassed) { + complement = " (while checking file existence in server)"; + } else if (!localCopyPassed) { + complement = " (while copying local file to " + FileStorageUtils.getSavePath(mAccount.name) + + ")"; + } + Log_OC.e(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage() + complement, result.getException()); + } else { + Log_OC.e(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage()); + } + } } - + return result; } - + private void createNewOCFile(String newRemotePath) { + // a new OCFile instance must be created for a new remote path + OCFile newFile = new OCFile(newRemotePath); + newFile.setCreationTimestamp(mFile.getCreationTimestamp()); + newFile.setFileLength(mFile.getFileLength()); + newFile.setMimetype(mFile.getMimetype()); + newFile.setModificationTimestamp(mFile.getModificationTimestamp()); + newFile.setModificationTimestampAtLastSyncForData(mFile.getModificationTimestampAtLastSyncForData()); + // newFile.setEtag(mFile.getEtag()) + newFile.setKeepInSync(mFile.keepInSync()); + newFile.setLastSyncDateForProperties(mFile.getLastSyncDateForProperties()); + newFile.setLastSyncDateForData(mFile.getLastSyncDateForData()); + newFile.setStoragePath(mFile.getStoragePath()); + newFile.setParentId(mFile.getParentId()); + mOldFile = mFile; + mFile = newFile; + } + public boolean isSuccess(int status) { return ((status == HttpStatus.SC_OK || status == HttpStatus.SC_CREATED || status == HttpStatus.SC_NO_CONTENT)); } - - - protected int uploadFile(WebdavClient client) throws HttpException, IOException { + + protected int uploadFile(WebdavClient client) throws HttpException, IOException, OperationCancelledException { int status = -1; - PutMethod put = new PutMethod(client.getBaseUri() + WebdavUtils.encodePath(mRemotePath)); - try { - File f = new File(mLocalPath); - FileRequestEntity entity = new FileRequestEntity(f, mMimeType); - entity.addOnDatatransferProgressListeners(mDataTransferListeners); - put.setRequestEntity(entity); - status = client.executeMethod(put); - client.exhaustResponse(put.getResponseBodyAsStream()); - + File f = new File(mFile.getStoragePath()); + mEntity = new FileRequestEntity(f, getMimeType()); + synchronized (mDataTransferListeners) { + ((ProgressiveDataTransferer)mEntity).addDatatransferProgressListeners(mDataTransferListeners); + } + mPutMethod.setRequestEntity(mEntity); + status = client.executeMethod(mPutMethod); + client.exhaustResponse(mPutMethod.getResponseBodyAsStream()); + } finally { - put.releaseConnection(); // let the connection available for other methods + mPutMethod.releaseConnection(); // let the connection available for + // other methods } return status; } - + /** - * Checks if remotePath does not exist in the server and returns it, or adds a suffix to it in order to avoid the server - * file is overwritten. + * Checks if remotePath does not exist in the server and returns it, or adds + * a suffix to it in order to avoid the server file is overwritten. * * @param string * @return @@ -185,12 +393,12 @@ public class UploadFileOperation extends RemoteOperation { if (!check) { return remotePath; } - + int pos = remotePath.lastIndexOf("."); String suffix = ""; String extension = ""; if (pos >= 0) { - extension = remotePath.substring(pos+1); + extension = remotePath.substring(pos + 1); remotePath = remotePath.substring(0, pos); } int count = 2; @@ -203,12 +411,19 @@ public class UploadFileOperation extends RemoteOperation { count++; } while (check); - if (pos >=0) { + if (pos >= 0) { return remotePath + suffix + "." + extension; } else { return remotePath + suffix; } } + public void cancel() { + synchronized (mCancellationRequested) { + mCancellationRequested.set(true); + if (mPutMethod != null) + mPutMethod.abort(); + } + } }