// METADATA private void GetMetadata <T>(string dropboxPath, Action <DropboxRequestResult <T> > onResult) where T : DBXItem { var prms = new DropboxGetMetadataRequestParams(dropboxPath); Log("GetMetadata for " + dropboxPath); MakeDropboxRequest(METADATA_ENDPOINT, prms, onResponse: (jsonStr) => { Log("GetMetadata onResponse"); var dict = JSON.FromJson <Dictionary <string, object> >(jsonStr); if (typeof(T) == typeof(DBXFolder)) { var folderMetadata = DBXFolder.FromDropboxDictionary(dict); onResult(new DropboxRequestResult <T>(folderMetadata as T)); } else if (typeof(T) == typeof(DBXFile)) { var fileMetadata = DBXFile.FromDropboxDictionary(dict); onResult(new DropboxRequestResult <T>(fileMetadata as T)); } }, onProgress: null, onWebError: (error) => { Log("GetMetadata:onWebError"); onResult(DropboxRequestResult <T> .Error(error)); }); }
// GETTING REMOTE CHANGES void FolderGetRemoteChanges(string dropboxFolderPath, Action <DropboxRequestResult <List <DBXFileChange> > > onResult, bool saveChangesInfoLocally = false) { GetFolderItems(dropboxFolderPath, onResult: (res) => { if (res.error != null) { onResult(DropboxRequestResult <List <DBXFileChange> > .Error(res.error)); } else { var fileChanges = new List <DBXFileChange>(); foreach (DBXFile remoteMetadata in res.data.Where(x => x.type == DBXItemType.File)) { var localMetadata = GetLocalMetadataForFile(remoteMetadata.path); if (localMetadata != null && !localMetadata.deletedOnRemote) { if (localMetadata.contentHash != remoteMetadata.contentHash) { fileChanges.Add(new DBXFileChange(remoteMetadata, DBXFileChangeType.Modified)); } } else { // no local metadata for this remote path - new object fileChanges.Add(new DBXFileChange(remoteMetadata, DBXFileChangeType.Added)); } } // find other local files which were not in remote response (find deleted on remote files) var processedDropboxFilePaths = res.data.Where(x => x.type == DBXItemType.File).Select(x => x.path).ToList(); //Log("Find all metadata paths"); var localDirectoryPath = GetPathInCache(dropboxFolderPath); if (Directory.Exists(localDirectoryPath)) { foreach (string localMetadataFilePath in Directory.GetFiles(localDirectoryPath, "*.dbxsync", SearchOption.AllDirectories)) { var metadata = ParseLocalMetadata(localMetadataFilePath); var dropboxPath = metadata.path; if (!processedDropboxFilePaths.Contains(dropboxPath) && !metadata.deletedOnRemote) { // wasnt in remote data - means removed fileChanges.Add(new DBXFileChange(DBXFile.DeletedOnRemote(dropboxPath), DBXFileChangeType.Deleted)); } } } if (saveChangesInfoLocally) { foreach (var fc in fileChanges) { SaveFileMetadata(fc.file); } } onResult(new DropboxRequestResult <List <DBXFileChange> >(fileChanges)); } }, recursive: true, onProgress: null); }
/// <summary> /// Subscribes to file changes on Dropbox. /// Callback fires once, when change is being registered and changed file checksum is cached in local metadata. /// If change was made not during app runtime, callback fires as soon as app is running and checking for updates. /// Update interval can be changed using `DBXChangeForChangesIntervalSeconds` (default values if 5 seconds). /// </summary> /// <param name="dropboxFilePath"> /// Path to file on Dropbox or inside Dropbox App (depending on accessToken type). /// Should start with "/". Example: /DropboxSyncExampleFolder/image.jpg /// </param> /// <param name="onChange"> /// Callback function that receives `DBXFileChange` that contains `changeType` and `DBXFile` (updated file metadata). /// </param> public void SubscribeToFileChanges(string dropboxFilePath, Action <DBXFileChange> onChange) { var item = new DBXFile(dropboxFilePath); SubscribeToChanges(item, (changes) => { onChange(changes[0]); }); }
void SaveFileMetadata(DBXFile fileMetadata) { var localFilePath = GetPathInCache(fileMetadata.path); // make sure containing directory exists var fileDirectoryPath = Path.GetDirectoryName(localFilePath); //Log("Local cached directory path: "+fileDirectoryPath); Directory.CreateDirectory(fileDirectoryPath); // write metadata to separate file near var newMetadataFilePath = GetMetadataFilePath(fileMetadata.path); File.WriteAllText(newMetadataFilePath, JsonUtility.ToJson(fileMetadata)); //Log("Wrote metadata file "+newMetadataFilePath); }
/// <summary> /// Moves file or folder from dropboxFromPath to dropboxToPath /// </summary> /// <param name="dropboxFromPath">From path</param> /// <param name="dropboxToPath">To path</param> /// <param name="onResult">Result callback containing metadata of moved object</param> public void Move(string dropboxFromPath, string dropboxToPath, Action <DropboxRequestResult <DBXItem> > onResult) { var prms = new DropboxMoveFileRequestParams(); prms.from_path = dropboxFromPath; prms.to_path = dropboxToPath; MakeDropboxRequest(MOVE_ENDPOINT, prms, (jsonStr) => { DBXItem metadata = null; try { var root = JSON.FromJson <Dictionary <string, object> >(jsonStr); var metadata_dict = root["metadata"] as Dictionary <string, object>; if (metadata_dict[".tag"].ToString() == "file") { metadata = DBXFile.FromDropboxDictionary(metadata_dict); } else if (metadata_dict[".tag"].ToString() == "folder") { metadata = DBXFolder.FromDropboxDictionary(metadata_dict); } }catch (Exception ex) { _mainThreadQueueRunner.QueueOnMainThread(() => { onResult(DropboxRequestResult <DBXItem> .Error(new DBXError(ex.Message, DBXErrorType.ParsingError))); }); return; } _mainThreadQueueRunner.QueueOnMainThread(() => { onResult(new DropboxRequestResult <DBXItem>(metadata)); }); }, onProgress: (progress) => {}, onWebError: (error) => { _mainThreadQueueRunner.QueueOnMainThread(() => { if (error.ErrorType == DBXErrorType.RemotePathAlreadyExists) { error.ErrorDescription = "Can't move file: " + dropboxToPath + " already exists"; } onResult(DropboxRequestResult <DBXItem> .Error(error)); }); }); }
// FILE OPERATIONS /// <summary> /// Deletes file or folder on Dropbox /// </summary> /// <param name="dropboxPath">Path to file or folder on Dropbox or inside of Dropbox App folder (depending on accessToken type). Should start with "/". Example:/DropboxSyncExampleFolder/image.jpg</param> /// <param name="onResult">Callback function that receives DropboxRequestResult with DBXItem metadata of deleted file or folder</param> public void Delete(string dropboxPath, Action <DropboxRequestResult <DBXItem> > onResult) { var prms = new DropboxDeletePathRequestParams(); prms.path = dropboxPath; MakeDropboxRequest(DELETE_ENDPOINT, prms, (jsonStr) => { DBXItem metadata = null; try { var root = JSON.FromJson <Dictionary <string, object> >(jsonStr); var metadata_dict = root["metadata"] as Dictionary <string, object>; if (metadata_dict[".tag"].ToString() == "file") { metadata = DBXFile.FromDropboxDictionary(metadata_dict); } else if (metadata_dict[".tag"].ToString() == "folder") { metadata = DBXFolder.FromDropboxDictionary(metadata_dict); } }catch (Exception ex) { _mainThreadQueueRunner.QueueOnMainThread(() => { onResult(DropboxRequestResult <DBXItem> .Error(new DBXError(ex.Message, DBXErrorType.ParsingError))); }); return; } _mainThreadQueueRunner.QueueOnMainThread(() => { onResult(new DropboxRequestResult <DBXItem>(metadata)); }); }, onProgress: (progress) => {}, onWebError: (error) => { _mainThreadQueueRunner.QueueOnMainThread(() => { onResult(DropboxRequestResult <DBXItem> .Error(error)); }); }); }
void UpdateFileFromRemote(DBXFile dropboxFile, Action onSuccess, Action <float> onProgress, Action <DBXError> onError) { UpdateFileFromRemote(dropboxFile.path, onSuccess, onProgress, onError); }
public DBXFileChange(DBXFile f, DBXFileChangeType c) { file = f; changeType = c; }
void MakeDropboxUploadRequest(string url, byte[] dataToUpload, string jsonParameters, Action <DBXFile> onResponse, Action <float> onProgress, Action <DBXError> onWebError) { DropboxSyncUtils.IsOnlineAsync((isOnline) => { if (!isOnline) { onWebError(new DBXError("No internet connection", DBXErrorType.NetworkProblem)); return; } try { using (var client = new DBXWebClient()){ client.Headers.Set("Authorization", "Bearer " + DropboxAccessToken); client.Headers.Set("Dropbox-API-Arg", jsonParameters); client.Headers.Set("Content-Type", "application/octet-stream"); client.UploadProgressChanged += (s, e) => { Log(string.Format("Upload {0} bytes out of {1} ({2}%)", e.BytesSent, e.TotalBytesToSend, e.ProgressPercentage)); if (onProgress != null) { // _mainThreadQueueRunner.QueueOnMainThread(() => { var uploadProgress = (float)e.BytesSent / e.TotalBytesToSend; var SERVER_PROCESSING_PERCENTAG_VALUE = 0.99f; if (uploadProgress > SERVER_PROCESSING_PERCENTAG_VALUE && e.ProgressPercentage < 100) { // waiting for server to process uploaded file onProgress(SERVER_PROCESSING_PERCENTAG_VALUE); } else { onProgress(uploadProgress); } // }); } }; client.UploadDataCompleted += (s, e) => { Log("MakeDropboxUploadRequest -> UploadDataCompleted"); _activeWebClientsList.Remove(client); if (e.Error != null) { Log("MakeDropboxUploadRequest -> UploadDataCompleted -> with error"); if (e.Error is WebException) { Log("MakeDropboxUploadRequest -> UploadDataCompleted -> with error -> WebException"); var webex = e.Error as WebException; var stream = webex.Response.GetResponseStream(); var reader = new StreamReader(stream); var responseStr = reader.ReadToEnd(); LogWarning(responseStr); try{ var dict = JSON.FromJson <Dictionary <string, object> >(responseStr); var errorSummary = dict["error_summary"].ToString(); // _mainThreadQueueRunner.QueueOnMainThread(() => { onWebError(new DBXError(errorSummary, DBXErrorType.DropboxAPIError)); // }); }catch { // _mainThreadQueueRunner.QueueOnMainThread(() => { onWebError(new DBXError(e.Error.Message, DBXErrorType.ParsingError)); // }); } } else { // _mainThreadQueueRunner.QueueOnMainThread(() => { Log("e.Error is something else"); if (e.Error != null) { onWebError(new DBXError(e.Error.Message, DBXErrorType.Unknown)); } else { onWebError(new DBXError("Unknown error", DBXErrorType.Unknown)); } // }); } } else if (e.Cancelled) { Log("MakeDropboxUploadRequest -> canceled"); // _mainThreadQueueRunner.QueueOnMainThread(() => { onWebError(new DBXError("Download was cancelled.", DBXErrorType.UserCancelled)); // }); } else { Log("MakeDropboxUploadRequest -> no error"); //var respStr = Encoding.UTF8.GetString(e.Result); var metadataJsonStr = Encoding.UTF8.GetString(e.Result);; Log(metadataJsonStr); var dict = JSON.FromJson <Dictionary <string, object> >(metadataJsonStr); var fileMetadata = DBXFile.FromDropboxDictionary(dict); // _mainThreadQueueRunner.QueueOnMainThread(() => { onResponse(fileMetadata); // }); } }; var uri = new Uri(url); // don't use UploadFile (https://stackoverflow.com/questions/18539807/how-to-remove-multipart-form-databoundary-from-webclient-uploadfile) client.UploadDataAsync(uri, "POST", dataToUpload); _activeWebClientsList.Add(client); } } catch (WebException ex) { // _mainThreadQueueRunner.QueueOnMainThread(() => { onWebError(new DBXError(ex.Message, DBXErrorType.Unknown)); // }); } }); }
void MakeDropboxDownloadRequest(string url, string jsonParameters, Action <DBXFile, byte[]> onResponse, Action <float> onProgress, Action <DBXError> onWebError) { DropboxSyncUtils.IsOnlineAsync((isOnline) => { if (!isOnline) { onWebError(new DBXError("No internet connection", DBXErrorType.NetworkProblem)); return; } try { using (var client = new DBXWebClient()){ client.Headers.Set("Authorization", "Bearer " + DropboxAccessToken); client.Headers.Set("Dropbox-API-Arg", jsonParameters); client.DownloadProgressChanged += (s, e) => { if (onProgress != null) { //Log(string.Format("Downloaded {0} bytes out of {1} ({2}%)", e.BytesReceived, e.TotalBytesToReceive, e.ProgressPercentage)); if (e.TotalBytesToReceive != -1) { // if download size in known from server // _mainThreadQueueRunner.QueueOnMainThread(() => { onProgress((float)e.BytesReceived / e.TotalBytesToReceive); // }); } else { // return progress is going but unknown // _mainThreadQueueRunner.QueueOnMainThread(() => { onProgress(-1); // }); } } }; client.DownloadDataCompleted += (s, e) => { _activeWebClientsList.Remove(client); if (e.Error != null) { if (e.Error is WebException) { var webex = e.Error as WebException; var stream = webex.Response.GetResponseStream(); var reader = new StreamReader(stream); var responseStr = reader.ReadToEnd(); Log(responseStr); try{ var dict = JSON.FromJson <Dictionary <string, object> >(responseStr); var errorSummary = dict["error_summary"].ToString(); // _mainThreadQueueRunner.QueueOnMainThread(() => { onWebError(new DBXError(errorSummary, DBXError.DropboxAPIErrorSummaryToErrorType(errorSummary))); // }); }catch { // _mainThreadQueueRunner.QueueOnMainThread(() => { onWebError(new DBXError(e.Error.Message, DBXErrorType.ParsingError)); // }); } } else { // _mainThreadQueueRunner.QueueOnMainThread(() => { Log("e.Error is something else"); if (e.Error != null) { onWebError(new DBXError(e.Error.Message, DBXErrorType.Unknown)); } else { onWebError(new DBXError("Unknown error", DBXErrorType.Unknown)); } // }); } } else if (e.Cancelled) { // _mainThreadQueueRunner.QueueOnMainThread(() => { onWebError(new DBXError("Download was cancelled.", DBXErrorType.UserCancelled)); // }); } else { //var respStr = Encoding.UTF8.GetString(e.Result); var metadataJsonStr = client.ResponseHeaders["Dropbox-API-Result"].ToString(); Log(metadataJsonStr); var dict = JSON.FromJson <Dictionary <string, object> >(metadataJsonStr); var fileMetadata = DBXFile.FromDropboxDictionary(dict); // _mainThreadQueueRunner.QueueOnMainThread(() => { onResponse(fileMetadata, e.Result); // }); } }; var uri = new Uri(url); client.DownloadDataAsync(uri); _activeWebClientsList.Add(client); } } catch (WebException ex) { // _mainThreadQueueRunner.QueueOnMainThread(() => { onWebError(new DBXError(ex.Message, DBXErrorType.Unknown)); // }); } }); }
void FileGetRemoteChanges(string dropboxFilePath, Action <DBXFileChange> onResult, Action <DBXError> onError, bool saveChangesInfoLocally = false) { var localFilePath = GetPathInCache(dropboxFilePath); var localFileExists = File.Exists(localFilePath); var metadataFilePath = GetMetadataFilePath(dropboxFilePath); var localMetadata = GetLocalMetadataForFile(dropboxFilePath); // request for metadata to get remote content hash //Log("Getting metadata"); GetMetadata <DBXFile>(dropboxFilePath, onResult: (res) => { DBXFileChange result = null; if (res.error != null) { Log("Failed to get remote metadata for file " + dropboxFilePath); if (res.error.ErrorType == DBXErrorType.RemotePathNotFound) { Log("file not found - file was deleted or moved"); // file was deleted or moved // if we knew about this file before if (localMetadata != null) { // if we didnt know that it was removed if (!localMetadata.deletedOnRemote) { result = new DBXFileChange(DBXFile.DeletedOnRemote(dropboxFilePath), DBXFileChangeType.Deleted); } else { // no change result = new DBXFileChange(localMetadata, DBXFileChangeType.None); } } else { onError(res.error); } } else { onError(res.error); return; } } else { Log("Got remote metadata for file " + dropboxFilePath); var remoteMedatadata = res.data; if (localMetadata != null && !localMetadata.deletedOnRemote) { Log("local metadata file exists and we knew this file existed on remote"); Log("check if remote content has changed"); // get local content hash // var local_content_hash = localMetadata.contentHash; string local_content_hash = null; if (localFileExists) { local_content_hash = DropboxSyncUtils.GetDropboxContentHashForFile(localFilePath); } else { local_content_hash = localMetadata.contentHash; } var remote_content_hash = remoteMedatadata.contentHash; if (local_content_hash != remote_content_hash) { Log("remote content hash has changed - file was modified"); result = new DBXFileChange(remoteMedatadata, DBXFileChangeType.Modified); } else { Log("remote content did not change"); result = new DBXFileChange(remoteMedatadata, DBXFileChangeType.None); } } else { // metadata file doesnt exist Log("local metadata file doesnt exist - consider as new file added"); // TODO: check maybe file itself exists and right version, then just create metadata file - no need to redownload file itself result = new DBXFileChange(remoteMedatadata, DBXFileChangeType.Added); } } // if no error if (result != null) { if (saveChangesInfoLocally) { SaveFileMetadata(result.file); } onResult(result); } }); }
void _GetFolderItemsFlat(string folderPath, Action <List <DBXItem> > onResult, Action <float> onProgress, Action <DBXError> onError, bool recursive = false, string requestCursor = null, List <DBXItem> currentResults = null) { folderPath = DropboxSyncUtils.NormalizePath(folderPath); if (folderPath == "/") { folderPath = ""; // dropbox error fix } string url; DropboxRequestParams prms; if (requestCursor == null) { // first request currentResults = new List <DBXItem>(); url = LIST_FOLDER_ENDPOINT; prms = new DropboxListFolderRequestParams { path = folderPath, recursive = recursive }; } else { // have cursor to continue list url = LIST_FOLDER_CONTINUE_ENDPOINT; prms = new DropboxContinueWithCursorRequestParams(requestCursor); } MakeDropboxRequest(url, prms, onResponse: (jsonStr) => { //Log("Got reponse: "+jsonStr); Dictionary <string, object> root = null; try { root = JSON.FromJson <Dictionary <string, object> >(jsonStr); }catch (Exception ex) { onError(new DBXError(ex.Message, DBXErrorType.ParsingError)); return; } var entries = root["entries"] as List <object>; foreach (Dictionary <string, object> entry in entries) { if (entry[".tag"].ToString() == "file") { currentResults.Add(DBXFile.FromDropboxDictionary(entry)); } else if (entry[".tag"].ToString() == "folder") { currentResults.Add(DBXFolder.FromDropboxDictionary(entry)); } else { onError(new DBXError("Unknown entry tag " + entry[".tag".ToString()], DBXErrorType.Unknown)); return; } } if ((bool)root["has_more"]) { // recursion _GetFolderItemsFlat(folderPath, onResult, onProgress, onError, recursive: recursive, requestCursor: root["cursor"].ToString(), currentResults: currentResults); } else { // done onResult(currentResults); } }, onProgress: onProgress, onWebError: (webErrorStr) => { //LogError("Got web err: "+webErrorStr); onError(webErrorStr); }); }