/// <summary> /// Uploads byte[] to specified Dropbox path /// </summary> /// <param name="dropboxPath">Dropbox path where to upload file. Example: /my_text.txt</param> /// <param name="bytes">Bytes array containing file data</param> /// <param name="onResult">Result callback that receives created remote file metadata</param> /// <param name="onProgress">Upload progress callback that receives float from 0 to 1</param> public void UploadFile(string dropboxPath, byte[] bytes, Action <DropboxRequestResult <DBXFile> > onResult, Action <float> onProgress = null) { var prms = new DropboxUploadFileRequestParams(dropboxPath); MakeDropboxUploadRequest(UPLOAD_FILE_ENDPOINT, bytes, prms, onResponse: (fileMetadata) => { _mainThreadQueueRunner.QueueOnMainThread(() => { onResult(new DropboxRequestResult <DBXFile>(fileMetadata)); }); }, onProgress: (progress) => { if (onProgress != null) { _mainThreadQueueRunner.QueueOnMainThread(() => { onProgress(progress); }); } }, onWebError: (webErrorStr) => { _mainThreadQueueRunner.QueueOnMainThread(() => { onResult(DropboxRequestResult <DBXFile> .Error(webErrorStr)); }); }); }
/// <summary> /// Asynchronously retrieves file from Dropbox and returns path to local filesystem cached copy.f /// </summary> /// <param name="dropboxPath">Path to file on Dropbox or inside of Dropbox App folder (depending on accessToken type). Should start with "/". Example: /DropboxSyncExampleFolder/image.jpg</param> /// <param name="onResult">Result callback</param> /// <param name="onProgress">Callback function that receives progress as float from 0 to 1.</param> /// <param name="useCachedFirst">If True then first tries to get data from cache, if not cached then downloads.</param> /// <param name="useCachedIfOffline">If True and there's no Internet connection then retrieves file from cache if cached, otherwise produces error.</param> /// <param name="receiveUpdates">If True, then when there are remote updates on Dropbox, callback function onResult will be triggered again with updated version of the file.</param> public void GetFileAsLocalCachedPath(string dropboxPath, Action <DropboxRequestResult <string> > onResult, Action <float> onProgress = null, bool useCachedFirst = false, bool useCachedIfOffline = true, bool receiveUpdates = false) { Action <DropboxRequestResult <byte[]> > onResultMiddle = (res) => { if (res.error != null) { _mainThreadQueueRunner.QueueOnMainThread(() => { onResult(DropboxRequestResult <string> .Error(res.error)); }); } else { if (res.data != null) { _mainThreadQueueRunner.QueueOnMainThread(() => { onResult(new DropboxRequestResult <string>(GetPathInCache(dropboxPath))); }); } else { _mainThreadQueueRunner.QueueOnMainThread(() => { onResult(new DropboxRequestResult <string>(null)); }); } } }; GetFileAsBytes(dropboxPath, onResultMiddle, onProgress, useCachedFirst, useCachedIfOffline, receiveUpdates); }
// 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)); }); }
// UPLOADING FILE /// <summary> /// Uploads file from specified filepath in local filesystem to Dropbox /// </summary> /// <param name="dropboxPath">Dropbox path where to upload file. Example: /my_text.txt</param> /// <param name="localFilePath">Full file path in local filesystem. Example: C:\my_text.txt</param> /// <param name="onResult">Result callback that receives created remote file metadata</param> /// <param name="onProgress">Upload progress callback that receives float from 0 to 1</param> public void UploadFile(string dropboxPath, string localFilePath, Action <DropboxRequestResult <DBXFile> > onResult, Action <float> onProgress = null) { // chec if specified local file path exists if (!File.Exists(localFilePath)) { onResult(DropboxRequestResult <DBXFile> .Error( new DBXError("Local file " + localFilePath + " does not exist.", DBXErrorType.LocalPathNotFound) ) ); return; } // read file bytes byte[] fileBytes = null; try{ fileBytes = File.ReadAllBytes(localFilePath); }catch (Exception ex) { onResult(DropboxRequestResult <DBXFile> .Error( new DBXError("Failed to read local file " + localFilePath + ": " + ex.Message, DBXErrorType.LocalFileSystemError) ) ); return; } // upload that bytes UploadFile(dropboxPath, fileBytes, onResult, onProgress); }
// 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> /// Creates folder using path specified /// </summary> /// <param name="dropboxFolderPath">Path of folder to create</param> /// <param name="onResult">Result callback that contains metadata of the created folder</param> public void CreateFolder(string dropboxFolderPath, Action <DropboxRequestResult <DBXFolder> > onResult) { var path = DropboxSyncUtils.NormalizePath(dropboxFolderPath); var prms = new DropboxCreateFolderRequestParams(); prms.path = path; MakeDropboxRequest(CREATE_FOLDER_ENDPOINT, prms, (jsonStr) => { DBXFolder folderMetadata = null; try { var root = JSON.FromJson <Dictionary <string, object> >(jsonStr); folderMetadata = DBXFolder.FromDropboxDictionary(root["metadata"] as Dictionary <string, object>); }catch (Exception ex) { _mainThreadQueueRunner.QueueOnMainThread(() => { onResult(DropboxRequestResult <DBXFolder> .Error(new DBXError(ex.Message, DBXErrorType.ParsingError))); }); return; } _mainThreadQueueRunner.QueueOnMainThread(() => { onResult(new DropboxRequestResult <DBXFolder>(folderMetadata)); }); }, onProgress: (progress) => {}, onWebError: (error) => { if (error.ErrorDescription.Contains("path/conflict/folder")) { error.ErrorType = DBXErrorType.RemotePathAlreadyExists; } _mainThreadQueueRunner.QueueOnMainThread(() => { onResult(DropboxRequestResult <DBXFolder> .Error(error)); }); }); }
// FOLDERS /// <summary> /// Checks if dropbox path (file or folder) exists /// </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">Result callback containing bool that indicates existance on the item</param> public void PathExists(string dropboxPath, Action <DropboxRequestResult <bool> > onResult) { GetMetadata <DBXFolder>(dropboxPath, (res) => { if (res.error != null) { if (res.error.ErrorType == DBXErrorType.RemotePathNotFound) { // path not found _mainThreadQueueRunner.QueueOnMainThread(() => { onResult(new DropboxRequestResult <bool>(false)); }); } else { // some other error _mainThreadQueueRunner.QueueOnMainThread(() => { onResult(DropboxRequestResult <bool> .Error(res.error)); }); } } else { // path exists _mainThreadQueueRunner.QueueOnMainThread(() => { onResult(new DropboxRequestResult <bool>(true)); }); } }); }
/// <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)); }); }); }
/// <summary> /// Gets files and folders inside specified folder as a list without structure. /// </summary> /// <param name="path">Path to folder</param> /// <param name="onResult">Vallback function that receives result containing list of DBXItems</param> /// <param name="onProgress">Callback fnction that receives float from 0 to 1 intdicating the progress.</param> /// <param name="recursive">If True then gets all items recursively.</param> public void GetFolderItems(string path, Action <DropboxRequestResult <List <DBXItem> > > onResult, Action <float> onProgress = null, bool recursive = false) { _GetFolderItemsFlat(path, onResult: (items) => { _mainThreadQueueRunner.QueueOnMainThread(() => { onResult(new DropboxRequestResult <List <DBXItem> >(items)); }); }, onProgress: (progress) => { if (onProgress != null) { _mainThreadQueueRunner.QueueOnMainThread(() => { onProgress(progress); }); } }, onError: (errorStr) => { _mainThreadQueueRunner.QueueOnMainThread(() => { onResult(DropboxRequestResult <List <DBXItem> > .Error(errorStr)); }); }, recursive: recursive); }
/// <summary> /// Retrieves structure of dropbox folders and files inside specified folder. /// </summary> /// <param name="dropboxFolderPath">Dropbox folder path</param> /// <param name="onResult">Callback function that receives result containing DBXFolder with all child nodes inside.</param> /// <param name="onProgress">Callback fnction that receives float from 0 to 1 intdicating the progress.</param> public void GetFolderStructure(string dropboxFolderPath, Action <DropboxRequestResult <DBXFolder> > onResult, Action <float> onProgress = null) { var path = DropboxSyncUtils.NormalizePath(dropboxFolderPath); _GetFolderItemsFlat(path, onResult: (items) => { DBXFolder rootFolder = null; // get root folder if (path == "/") { rootFolder = new DBXFolder { id = "", path = "/", name = "", items = new List <DBXItem>() }; } else { rootFolder = items.Where(x => x.path == path).First() as DBXFolder; } // squash flat results rootFolder = BuildStructureFromFlat(rootFolder, items); _mainThreadQueueRunner.QueueOnMainThread(() => { onResult(new DropboxRequestResult <DBXFolder>(rootFolder)); }); }, onProgress: (progress) => { if (onProgress != null) { _mainThreadQueueRunner.QueueOnMainThread(() => { onProgress(progress); }); } }, onError: (errorStr) => { _mainThreadQueueRunner.QueueOnMainThread(() => { onResult(DropboxRequestResult <DBXFolder> .Error(errorStr)); }); }, recursive: true); }
// 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)); }); }); }
// GETTING FILE /// <summary> /// Asynchronously retrieves file from Dropbox and tries to produce object of specified type T. /// </summary> /// <param name="dropboxPath">Path to file on Dropbox or inside of Dropbox App folder (depending on accessToken type). Should start with "/". Example: /DropboxSyncExampleFolder/image.jpg</param> /// <param name="onResult">Result callback</param> /// <param name="onProgress">Callback function that receives progress as float from 0 to 1.</param> /// <param name="useCachedFirst">If True then first tries to get data from cache, if not cached then downloads.</param> /// <param name="useCachedIfOffline">If True and there's no Internet connection then retrieves file from cache if cached, otherwise produces error.</param> /// <param name="receiveUpdates">If True, then when there are remote updates on Dropbox, callback function onResult will be triggered again with updated version of the file.</param> public void GetFile <T>(string dropboxPath, Action <DropboxRequestResult <T> > onResult, Action <float> onProgress = null, bool useCachedFirst = false, bool useCachedIfOffline = true, bool receiveUpdates = false) where T : class { Action <DropboxRequestResult <byte[]> > onResultMiddle = null; if (typeof(T) == typeof(string)) { //Log("GetFile: text type"); // TEXT DATA onResultMiddle = (res) => { if (res.error != null || res.data == null) { _mainThreadQueueRunner.QueueOnMainThread(() => { onResult(DropboxRequestResult <T> .Error(res.error)); }); } else { _mainThreadQueueRunner.QueueOnMainThread(() => { onResult(new DropboxRequestResult <T>(DropboxSyncUtils.GetAutoDetectedEncodingStringFromBytes(res.data) as T)); }); } }; } else if (typeof(T) == typeof(JsonObject) || typeof(T) == typeof(JsonArray)) { //Log("GetFile: JSON type"); // JSON OBJECT/ARRAY onResultMiddle = (res) => { if (res.error != null) { _mainThreadQueueRunner.QueueOnMainThread(() => { onResult(DropboxRequestResult <T> .Error(res.error)); }); } else { _mainThreadQueueRunner.QueueOnMainThread(() => { onResult(new DropboxRequestResult <T>(JSON.FromJson <T>( DropboxSyncUtils.GetAutoDetectedEncodingStringFromBytes(res.data) ))); }); } }; } else if (typeof(T) == typeof(Texture2D)) { //Log("GetFile: Texture2D type"); // IMAGE DATA onResultMiddle = (res) => { if (res.error != null) { _mainThreadQueueRunner.QueueOnMainThread(() => { onResult(DropboxRequestResult <T> .Error(res.error)); }); } else { _mainThreadQueueRunner.QueueOnMainThread(() => { onResult(new DropboxRequestResult <T>(DropboxSyncUtils.LoadImageToTexture2D(res.data) as T)); }); } }; } else { _mainThreadQueueRunner.QueueOnMainThread(() => { onResult(DropboxRequestResult <T> .Error( new DBXError(string.Format("Dont have a mapping byte[] -> {0}. Type {0} is not supported.", typeof(T).ToString()), DBXErrorType.NotSupported ) ) ); }); return; } GetFileAsBytes(dropboxPath, onResultMiddle, onProgress, useCachedFirst, useCachedIfOffline, receiveUpdates); }
/// <summary> /// Asynchronously retrieves file from Dropbox as byte[] /// </summary> /// <param name="dropboxPath">Path to file on Dropbox or inside of Dropbox App folder (depending on accessToken type). Should start with "/". Example: /DropboxSyncExampleFolder/image.jpg</param> /// <param name="onResult">Result callback</param> /// <param name="onProgress">Callback function that receives progress as float from 0 to 1.</param> /// <param name="useCachedFirst">If True then first tries to get data from cache, if not cached then downloads.</param> /// <param name="useCachedIfOffline">If True and there's no Internet connection then retrieves file from cache if cached, otherwise produces error.</param> /// <param name="receiveUpdates">If true , then when there are remote updates on Dropbox, callback function onResult will be triggered again with updated version of the file.</param> public void GetFileAsBytes(string dropboxPath, Action <DropboxRequestResult <byte[]> > onResult, Action <float> onProgress = null, bool useCachedFirst = false, bool useCachedIfOffline = true, bool receiveUpdates = false) { if (DropboxSyncUtils.IsBadDropboxPath(dropboxPath)) { onResult(DropboxRequestResult <byte[]> .Error( new DBXError("Cant get file: bad path " + dropboxPath, DBXErrorType.BadRequest) ) ); return; } Action returnCachedResult = () => { var cachedFilePath = GetPathInCache(dropboxPath); if (File.Exists(cachedFilePath)) { var bytes = File.ReadAllBytes(cachedFilePath); _mainThreadQueueRunner.QueueOnMainThread(() => { onResult(new DropboxRequestResult <byte[]>(bytes)); }); } else { Log("cache doesnt have file"); _mainThreadQueueRunner.QueueOnMainThread(() => { onResult( DropboxRequestResult <byte[]> .Error( new DBXError("File " + dropboxPath + " is removed on remote", DBXErrorType.RemotePathNotFound) ) ); }); } }; Action subscribeToUpdatesAction = () => { SubscribeToFileChanges(dropboxPath, (fileChange) => { UpdateFileFromRemote(dropboxPath, onSuccess: () => { // return updated cached result returnCachedResult(); }, onProgress: (progress) => { if (onProgress != null) { _mainThreadQueueRunner.QueueOnMainThread(() => { onProgress(progress); }); } }, onError: (error) => { _mainThreadQueueRunner.QueueOnMainThread(() => { onResult(DropboxRequestResult <byte[]> .Error(error)); }); }); }); }; // maybe no need to do any remote requests if ((useCachedFirst) && IsFileCached(dropboxPath)) { Log("GetFile: using cached version"); returnCachedResult(); if (receiveUpdates) { subscribeToUpdatesAction(); } } else { //Log("GetFile: check if online"); // now check if we online DropboxSyncUtils.IsOnlineAsync((isOnline) => { try { if (isOnline) { Log("GetFile: internet available"); // check if have updates and load them UpdateFileFromRemote(dropboxPath, onSuccess: () => { Log("GetFile: state of dropbox file is " + dropboxPath + " is synced now"); // return updated cached result returnCachedResult(); if (receiveUpdates) { subscribeToUpdatesAction(); } }, onProgress: (progress) => { if (onProgress != null) { _mainThreadQueueRunner.QueueOnMainThread(() => { onProgress(progress); }); } }, onError: (error) => { //Log("error"); _mainThreadQueueRunner.QueueOnMainThread(() => { onResult(DropboxRequestResult <byte[]> .Error(error)); }); if (receiveUpdates) { subscribeToUpdatesAction(); } }); } else { Log("GetFile: internet not available"); if (useCachedIfOffline && IsFileCached(dropboxPath)) { Log("GetFile: cannot check for updates - using cached version"); returnCachedResult(); if (receiveUpdates) { subscribeToUpdatesAction(); } } else { if (receiveUpdates) { // try again when internet recovers _internetConnectionWatcher.SubscribeToInternetConnectionRecoverOnce(() => { GetFileAsBytes(dropboxPath, onResult, onProgress, useCachedFirst, useCachedIfOffline, receiveUpdates); }); subscribeToUpdatesAction(); } else { // error _mainThreadQueueRunner.QueueOnMainThread(() => { onResult(DropboxRequestResult <byte[]> .Error( new DBXError("GetFile: No internet connection", DBXErrorType.NetworkProblem) ) ); }); } } } }catch (Exception ex) { Debug.LogException(ex); } }); } }