public void ExportBackground(ExportDataRequest request, ExportPushNotification notification, IJobCancellationToken cancellationToken, PerformContext context) { void progressCallback(ExportProgressInfo x) { notification.Patch(x); notification.JobId = context.BackgroundJob.Id; _pushNotificationManager.Upsert(notification); } try { var localTmpFolder = HostingEnvironment.MapPath(_defaultExportFolder); var fileName = Path.Combine(localTmpFolder, string.Format(FileNameTemplate, DateTime.UtcNow)); // Do not like provider creation here to get file extension, maybe need to pass created provider to Exporter. // Create stream inside Exporter is not good as it is not Exporter resposibility to decide where to write. var provider = _exportProviderFactory.CreateProvider(request); if (!string.IsNullOrEmpty(provider.ExportedFileExtension)) { fileName = Path.ChangeExtension(string.Format(FileNameTemplate, DateTime.UtcNow), provider.ExportedFileExtension); } var localTmpPath = Path.Combine(localTmpFolder, fileName); if (!Directory.Exists(localTmpFolder)) { Directory.CreateDirectory(localTmpFolder); } if (File.Exists(localTmpPath)) { File.Delete(localTmpPath); } //Import first to local tmp folder because Azure blob storage doesn't support some special file access mode using (var stream = File.OpenWrite(localTmpPath)) { _dataExporter.Export(stream, request, progressCallback, new JobCancellationTokenWrapper(cancellationToken)); notification.DownloadUrl = $"api/export/download/{fileName}"; } } catch (JobAbortedException) { //do nothing } catch (Exception ex) { notification.Errors.Add(ex.ExpandExceptionMessage()); } finally { notification.Description = "Export finished"; notification.Finished = DateTime.UtcNow; _pushNotificationManager.Upsert(notification); } }
public async Task ExportBackgroundAsync(ExportDataRequest request, ExportPushNotification notification, IJobCancellationToken cancellationToken, PerformContext context) { void progressCallback(ExportProgressInfo x) { notification.Patch(x); notification.JobId = context.BackgroundJob.Id; _pushNotificationManager.Send(notification); } try { if (string.IsNullOrEmpty(_platformOptions.DefaultExportFolder)) { throw new PlatformException($"{nameof(_platformOptions.DefaultExportFolder)} should be set."); } var fileName = string.Format(FileNameTemplate, DateTime.UtcNow); // Do not like provider creation here to get file extension, maybe need to pass created provider to Exporter. // Create stream inside Exporter is not good as it is not Exporter resposibility to decide where to write. var provider = _exportProviderFactory.CreateProvider(request); if (!string.IsNullOrEmpty(provider.ExportedFileExtension)) { fileName = Path.ChangeExtension(fileName, provider.ExportedFileExtension); } var url = UrlHelperExtensions.Combine(_platformOptions.DefaultExportFolder, fileName); using (var blobStream = _blobStorageProvider.OpenWrite(url)) { _dataExporter.Export(blobStream, request, progressCallback, new JobCancellationTokenWrapper(cancellationToken)); } notification.DownloadUrl = _blobUrlResolver.GetAbsoluteUrl(url); } catch (JobAbortedException) { //do nothing } catch (Exception ex) { notification.Errors.Add(ex.ExpandExceptionMessage()); } finally { notification.Description = "Export finished"; notification.Finished = DateTime.UtcNow; await _pushNotificationManager.SendAsync(notification); } }
public void Export(Stream stream, ExportDataRequest request, Action <ExportProgressInfo> progressCallback, ICancellationToken token) { if (request == null) { throw new ArgumentNullException(nameof(request)); } token.ThrowIfCancellationRequested(); var exportedTypeDefinition = _exportTypesResolver.ResolveExportedTypeDefinition(request.ExportTypeName); var pagedDataSource = (exportedTypeDefinition.DataSourceFactory ?? throw new ArgumentNullException(nameof(ExportedTypeDefinition.DataSourceFactory))).Create(request.DataQuery); var completedMessage = $"Export completed"; var totalCount = pagedDataSource.GetTotalCount(); var exportedCount = 0; var exportProgress = new ExportProgressInfo() { ProcessedCount = 0, TotalCount = totalCount, Description = "Export has started", }; progressCallback(exportProgress); try { exportProgress.Description = "Creating provider…"; progressCallback(exportProgress); using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true) { AutoFlush = true }) using (var exportProvider = _exportProviderFactory.CreateProvider(request)) { var needTabularData = exportProvider.IsTabular; if (needTabularData && !exportedTypeDefinition.IsTabularExportSupported) { throw new NotSupportedException($"Provider \"{exportProvider.TypeName}\" does not support tabular export."); } exportProgress.Description = "Fetching…"; progressCallback(exportProgress); while (pagedDataSource.Fetch()) { token.ThrowIfCancellationRequested(); var objectBatch = pagedDataSource.Items; foreach (var obj in objectBatch) { try { var preparedObject = obj.Clone() as IExportable; request.DataQuery.FilterProperties(preparedObject); if (needTabularData) { preparedObject = (preparedObject as ITabularConvertible)?.ToTabular() ?? throw new NotSupportedException($"Object should be {nameof(ITabularConvertible)} to be exported using tabular provider."); } exportProvider.WriteRecord(writer, preparedObject); } catch (Exception e) { exportProgress.Errors.Add(e.Message); progressCallback(exportProgress); } exportedCount++; } exportProgress.ProcessedCount = exportedCount; if (exportedCount != totalCount) { exportProgress.Description = $"{exportedCount} out of {totalCount} have been exported."; progressCallback(exportProgress); } } } } catch (Exception e) { exportProgress.Errors.Add(e.Message); } finally { if (exportProgress.Errors.Count > 0) { completedMessage = $"Export completed with errors"; } exportProgress.Description = $"{completedMessage}: {exportedCount} out of {totalCount} have been exported."; progressCallback(exportProgress); } }