ToolBox: Better error handling

This commit is contained in:
Genteure 2021-04-19 18:20:14 +08:00
parent 04cc54ee5c
commit cf5dae1851
12 changed files with 436 additions and 155 deletions

View File

@ -138,10 +138,10 @@ namespace BililiveRecorder.Flv.Parser
fileHeaderSlice.CopyTo(stackSpan); fileHeaderSlice.CopyTo(stackSpan);
if (data[0] != 'F' || data[1] != 'L' || data[2] != 'V' || data[3] != 1) if (data[0] != 'F' || data[1] != 'L' || data[2] != 'V' || data[3] != 1)
throw new FlvException("Data is not FLV."); throw new NotFlvFileException("Data is not FLV.");
if (data[5] != 0 || data[6] != 0 || data[7] != 0 || data[8] != 9) if (data[5] != 0 || data[6] != 0 || data[7] != 0 || data[8] != 9)
throw new FlvException("Not Supported FLV format."); throw new NotFlvFileException("Not Supported FLV format.");
buffer = buffer.Slice(fileHeaderSlice.End); buffer = buffer.Slice(fileHeaderSlice.End);
@ -180,7 +180,7 @@ namespace BililiveRecorder.Flv.Parser
case TagType.Script: case TagType.Script:
break; break;
default: default:
throw new FlvException("Unexpected Tag Type: " + header[0]); throw new UnknownFlvTagTypeException("Unknown Tag Type: " + header[0]);
} }
// Read Tag Size // Read Tag Size

View File

@ -0,0 +1,16 @@
using System;
using System.Runtime.Serialization;
namespace BililiveRecorder.Flv.Parser
{
public class NotFlvFileException : FlvException
{
public NotFlvFileException() { }
public NotFlvFileException(string message) : base(message) { }
public NotFlvFileException(string message, Exception innerException) : base(message, innerException) { }
protected NotFlvFileException(SerializationInfo info, StreamingContext context) : base(info, context) { }
}
}

View File

@ -0,0 +1,16 @@
using System;
using System.Runtime.Serialization;
namespace BililiveRecorder.Flv.Parser
{
public class UnknownFlvTagTypeException : FlvException
{
public UnknownFlvTagTypeException() { }
public UnknownFlvTagTypeException(string message) : base(message) { }
public UnknownFlvTagTypeException(string message, Exception innerException) : base(message, innerException) { }
protected UnknownFlvTagTypeException(SerializationInfo info, StreamingContext context) : base(info, context) { }
}
}

View File

@ -0,0 +1,18 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace BililiveRecorder.ToolBox
{
public class CommandResponse<TResponse> where TResponse : class
{
[JsonConverter(typeof(StringEnumConverter))]
public ResponseStatus Status { get; set; }
public TResponse? Result { get; set; }
public string? ErrorMessage { get; set; }
public Exception? Exception { get; set; }
}
}

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.IO.Compression;
using System.IO.Pipelines; using System.IO.Pipelines;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -10,6 +11,7 @@ using BililiveRecorder.Flv.Grouping;
using BililiveRecorder.Flv.Parser; using BililiveRecorder.Flv.Parser;
using BililiveRecorder.Flv.Pipeline; using BililiveRecorder.Flv.Pipeline;
using BililiveRecorder.Flv.Writer; using BililiveRecorder.Flv.Writer;
using BililiveRecorder.Flv.Xml;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Serilog; using Serilog;
@ -40,22 +42,56 @@ namespace BililiveRecorder.ToolBox.Commands
{ {
private static readonly ILogger logger = Log.ForContext<AnalyzeHandler>(); private static readonly ILogger logger = Log.ForContext<AnalyzeHandler>();
public Task<AnalyzeResponse> Handle(AnalyzeRequest request) => this.Handle(request, null); public Task<CommandResponse<AnalyzeResponse>> Handle(AnalyzeRequest request) => this.Handle(request, null);
public async Task<AnalyzeResponse> Handle(AnalyzeRequest request, Func<double, Task>? progress) public async Task<CommandResponse<AnalyzeResponse>> Handle(AnalyzeRequest request, Func<double, Task>? progress)
{
try
{ {
var inputPath = Path.GetFullPath(request.Input);
var memoryStreamProvider = new DefaultMemoryStreamProvider(); var memoryStreamProvider = new DefaultMemoryStreamProvider();
var tagWriter = new AnalyzeMockFlvTagWriter(); var tagWriter = new AnalyzeMockFlvTagWriter();
var comments = new List<ProcessingComment>(); var comments = new List<ProcessingComment>();
var context = new FlvProcessingContext(); var context = new FlvProcessingContext();
var session = new Dictionary<object, object?>(); var session = new Dictionary<object, object?>();
{ string? inputPath;
using var inputStream = File.OpenRead(inputPath); IFlvTagReader tagReader;
FileStream? flvFileStream = null;
using var grouping = new TagGroupReader(new FlvTagPipeReader(PipeReader.Create(inputStream), memoryStreamProvider, skipData: false, logger: logger)); try
{
inputPath = Path.GetFullPath(request.Input);
if (inputPath.EndsWith(".gz", StringComparison.OrdinalIgnoreCase))
tagReader = await Task.Run(() =>
{
using var stream = new GZipStream(File.Open(inputPath, FileMode.Open, FileAccess.Read, FileShare.Read), CompressionMode.Decompress);
var xmlFlvFile = (XmlFlvFile)XmlFlvFile.Serializer.Deserialize(stream);
return new FlvTagListReader(xmlFlvFile.Tags);
});
else if (inputPath.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
tagReader = await Task.Run(() =>
{
using var stream = File.Open(inputPath, FileMode.Open, FileAccess.Read, FileShare.Read);
var xmlFlvFile = (XmlFlvFile)XmlFlvFile.Serializer.Deserialize(stream);
return new FlvTagListReader(xmlFlvFile.Tags);
});
else
{
flvFileStream = File.Open(inputPath, FileMode.Open, FileAccess.Read, FileShare.Read);
tagReader = new FlvTagPipeReader(PipeReader.Create(flvFileStream), memoryStreamProvider, skipData: false, logger: logger);
}
}
catch (Exception ex) when (ex is not FlvException)
{
return new CommandResponse<AnalyzeResponse>
{
Status = ResponseStatus.InputIOError,
Exception = ex,
ErrorMessage = ex.Message
};
}
using var grouping = new TagGroupReader(tagReader);
using var writer = new FlvProcessingContextWriter(tagWriter: tagWriter, allowMissingHeader: true); using var writer = new FlvProcessingContextWriter(tagWriter: tagWriter, allowMissingHeader: true);
var pipeline = new ProcessingPipelineBuilder(new ServiceCollection().BuildServiceProvider()).AddDefault().AddRemoveFillerData().Build(); var pipeline = new ProcessingPipelineBuilder(new ServiceCollection().BuildServiceProvider()).AddDefault().AddRemoveFillerData().Build();
@ -82,16 +118,14 @@ namespace BililiveRecorder.ToolBox.Commands
foreach (var tag in dataAction.Tags) foreach (var tag in dataAction.Tags)
tag.BinaryData?.Dispose(); tag.BinaryData?.Dispose();
if (count++ % 10 == 0) if (count++ % 10 == 0 && flvFileStream is not null && progress is not null)
await progress((double)flvFileStream.Position / flvFileStream.Length);
}
var response = await Task.Run(() =>
{ {
progress?.Invoke((double)inputStream.Position / inputStream.Length); var countableComments = comments.Where(x => x.T != CommentType.Logging).ToArray();
} return new AnalyzeResponse
}
}
var countableComments = comments.Where(x => x.T != CommentType.Logging);
var response = new AnalyzeResponse
{ {
InputPath = inputPath, InputPath = inputPath,
@ -106,8 +140,41 @@ namespace BililiveRecorder.ToolBox.Commands
IssueTypeDecodingHeader = countableComments.Count(x => x.T == CommentType.DecodingHeader), IssueTypeDecodingHeader = countableComments.Count(x => x.T == CommentType.DecodingHeader),
IssueTypeRepeatingData = countableComments.Count(x => x.T == CommentType.RepeatingData) IssueTypeRepeatingData = countableComments.Count(x => x.T == CommentType.RepeatingData)
}; };
});
return response; return new CommandResponse<AnalyzeResponse>
{
Status = ResponseStatus.OK,
Result = response
};
}
catch (NotFlvFileException ex)
{
return new CommandResponse<AnalyzeResponse>
{
Status = ResponseStatus.NotFlvFile,
Exception = ex,
ErrorMessage = ex.Message
};
}
catch (UnknownFlvTagTypeException ex)
{
return new CommandResponse<AnalyzeResponse>
{
Status = ResponseStatus.UnknownFlvTagType,
Exception = ex,
ErrorMessage = ex.Message
};
}
catch (Exception ex)
{
return new CommandResponse<AnalyzeResponse>
{
Status = ResponseStatus.Error,
Exception = ex,
ErrorMessage = ex.Message
};
}
} }
public void PrintResponse(AnalyzeResponse response) public void PrintResponse(AnalyzeResponse response)

View File

@ -26,41 +26,103 @@ namespace BililiveRecorder.ToolBox.Commands
{ {
private static readonly ILogger logger = Log.ForContext<ExportHandler>(); private static readonly ILogger logger = Log.ForContext<ExportHandler>();
public Task<ExportResponse> Handle(ExportRequest request) => this.Handle(request, null); public Task<CommandResponse<ExportResponse>> Handle(ExportRequest request) => this.Handle(request, null);
public async Task<ExportResponse> Handle(ExportRequest request, Func<double, Task>? progress) public async Task<CommandResponse<ExportResponse>> Handle(ExportRequest request, Func<double, Task>? progress)
{ {
using var inputStream = File.OpenRead(request.Input); FileStream? inputStream = null, outputStream = null;
using var outputStream = File.OpenWrite(request.Output); try
{
var tags = new List<Tag>(); try
{
inputStream = File.Open(request.Input, FileMode.Open, FileAccess.Read, FileShare.Read);
}
catch (Exception ex)
{
return new CommandResponse<ExportResponse>
{
Status = ResponseStatus.InputIOError,
Exception = ex,
ErrorMessage = ex.Message
};
}
try
{
outputStream = File.OpenWrite(request.Output);
}
catch (Exception ex)
{
return new CommandResponse<ExportResponse>
{
Status = ResponseStatus.OutputIOError,
Exception = ex,
ErrorMessage = ex.Message
};
}
var tags = await Task.Run(async () =>
{ {
using var reader = new FlvTagPipeReader(PipeReader.Create(inputStream), new DefaultMemoryStreamProvider(), skipData: true, logger: logger);
var count = 0; var count = 0;
var tags = new List<Tag>();
using var reader = new FlvTagPipeReader(PipeReader.Create(inputStream), new DefaultMemoryStreamProvider(), skipData: true, logger: logger);
while (true) while (true)
{ {
var tag = await reader.ReadTagAsync(default).ConfigureAwait(false); var tag = await reader.ReadTagAsync(default).ConfigureAwait(false);
if (tag is null) break; if (tag is null) break;
tags.Add(tag); tags.Add(tag);
if (count++ % 300 == 0) if (count++ % 300 == 0 && progress is not null)
progress?.Invoke((double)inputStream.Position / inputStream.Length); await progress((double)inputStream.Position / inputStream.Length);
}
} }
return tags;
});
await Task.Run(() =>
{ {
using var writer = new StreamWriter(new GZipStream(outputStream, CompressionLevel.Optimal)); using var writer = new StreamWriter(new GZipStream(outputStream, CompressionLevel.Optimal));
XmlFlvFile.Serializer.Serialize(writer, new XmlFlvFile XmlFlvFile.Serializer.Serialize(writer, new XmlFlvFile
{ {
Tags = tags Tags = tags
}); });
});
return new CommandResponse<ExportResponse> { Status = ResponseStatus.OK, Result = new ExportResponse() };
}
catch (NotFlvFileException ex)
{
return new CommandResponse<ExportResponse>
{
Status = ResponseStatus.NotFlvFile,
Exception = ex,
ErrorMessage = ex.Message
};
}
catch (UnknownFlvTagTypeException ex)
{
return new CommandResponse<ExportResponse>
{
Status = ResponseStatus.UnknownFlvTagType,
Exception = ex,
ErrorMessage = ex.Message
};
}
catch (Exception ex)
{
return new CommandResponse<ExportResponse>
{
Status = ResponseStatus.Error,
Exception = ex,
ErrorMessage = ex.Message
};
}
finally
{
inputStream?.Dispose();
outputStream?.Dispose();
}
} }
return new ExportResponse(); public void PrintResponse(ExportResponse response) => Console.WriteLine("OK");
}
public void PrintResponse(ExportResponse response)
{ }
} }
} }

View File

@ -43,9 +43,12 @@ namespace BililiveRecorder.ToolBox.Commands
{ {
private static readonly ILogger logger = Log.ForContext<FixHandler>(); private static readonly ILogger logger = Log.ForContext<FixHandler>();
public Task<FixResponse> Handle(FixRequest request) => this.Handle(request, null); public Task<CommandResponse<FixResponse>> Handle(FixRequest request) => this.Handle(request, null);
public async Task<FixResponse> Handle(FixRequest request, Func<double, Task>? progress) public async Task<CommandResponse<FixResponse>> Handle(FixRequest request, Func<double, Task>? progress)
{
FileStream? inputStream = null;
try
{ {
var inputPath = Path.GetFullPath(request.Input); var inputPath = Path.GetFullPath(request.Input);
@ -61,7 +64,19 @@ namespace BililiveRecorder.ToolBox.Commands
var session = new Dictionary<object, object?>(); var session = new Dictionary<object, object?>();
{ {
using var inputStream = File.OpenRead(inputPath); try
{
inputStream = File.Open(inputPath, FileMode.Open, FileAccess.Read, FileShare.Read);
}
catch (Exception ex) when (ex is not FlvException)
{
return new CommandResponse<FixResponse>
{
Status = ResponseStatus.InputIOError,
Exception = ex,
ErrorMessage = ex.Message
};
}
using var grouping = new TagGroupReader(new FlvTagPipeReader(PipeReader.Create(inputStream), memoryStreamProvider, skipData: false, logger: logger)); using var grouping = new TagGroupReader(new FlvTagPipeReader(PipeReader.Create(inputStream), memoryStreamProvider, skipData: false, logger: logger));
using var writer = new FlvProcessingContextWriter(tagWriter: tagWriter, allowMissingHeader: true); using var writer = new FlvProcessingContextWriter(tagWriter: tagWriter, allowMissingHeader: true);
@ -90,16 +105,15 @@ namespace BililiveRecorder.ToolBox.Commands
foreach (var tag in dataAction.Tags) foreach (var tag in dataAction.Tags)
tag.BinaryData?.Dispose(); tag.BinaryData?.Dispose();
if (count++ % 10 == 0) if (count++ % 10 == 0 && progress is not null)
await progress((double)inputStream.Position / inputStream.Length);
}
}
var response = await Task.Run(() =>
{ {
progress?.Invoke((double)inputStream.Position / inputStream.Length); var countableComments = comments.Where(x => x.T != CommentType.Logging).ToArray();
} return new FixResponse
}
}
var countableComments = comments.Where(x => x.T != CommentType.Logging);
var response = new FixResponse
{ {
InputPath = inputPath, InputPath = inputPath,
OutputPaths = outputPaths.ToArray(), OutputPaths = outputPaths.ToArray(),
@ -114,8 +128,41 @@ namespace BililiveRecorder.ToolBox.Commands
IssueTypeDecodingHeader = countableComments.Count(x => x.T == CommentType.DecodingHeader), IssueTypeDecodingHeader = countableComments.Count(x => x.T == CommentType.DecodingHeader),
IssueTypeRepeatingData = countableComments.Count(x => x.T == CommentType.RepeatingData) IssueTypeRepeatingData = countableComments.Count(x => x.T == CommentType.RepeatingData)
}; };
});
return response; return new CommandResponse<FixResponse> { Status = ResponseStatus.OK, Result = response };
}
catch (NotFlvFileException ex)
{
return new CommandResponse<FixResponse>
{
Status = ResponseStatus.NotFlvFile,
Exception = ex,
ErrorMessage = ex.Message
};
}
catch (UnknownFlvTagTypeException ex)
{
return new CommandResponse<FixResponse>
{
Status = ResponseStatus.UnknownFlvTagType,
Exception = ex,
ErrorMessage = ex.Message
};
}
catch (Exception ex)
{
return new CommandResponse<FixResponse>
{
Status = ResponseStatus.Error,
Exception = ex,
ErrorMessage = ex.Message
};
}
finally
{
inputStream?.Dispose();
}
} }
public void PrintResponse(FixResponse response) public void PrintResponse(FixResponse response)

View File

@ -2,9 +2,11 @@ using System.Threading.Tasks;
namespace BililiveRecorder.ToolBox namespace BililiveRecorder.ToolBox
{ {
public interface ICommandHandler<TRequest, TResponse> where TRequest : ICommandRequest<TResponse> public interface ICommandHandler<TRequest, TResponse>
where TRequest : ICommandRequest<TResponse>
where TResponse : class
{ {
Task<TResponse> Handle(TRequest request); Task<CommandResponse<TResponse>> Handle(TRequest request);
void PrintResponse(TResponse response); void PrintResponse(TResponse response);
} }
} }

View File

@ -1,4 +1,6 @@
namespace BililiveRecorder.ToolBox namespace BililiveRecorder.ToolBox
{ {
public interface ICommandRequest<TResponse> { } public interface ICommandRequest<TResponse>
where TResponse : class
{ }
} }

View File

@ -0,0 +1,12 @@
namespace BililiveRecorder.ToolBox
{
public enum ResponseStatus
{
Error = 0,
OK,
NotFlvFile,
UnknownFlvTagType,
InputIOError,
OutputIOError,
}
}

View File

@ -29,25 +29,27 @@ namespace BililiveRecorder.ToolBox
}); });
} }
private void RegisterCommand<IHandler, IRequest, IResponse>(string name, string? description, Action<Command> configure) private void RegisterCommand<THandler, TRequest, TResponse>(string name, string? description, Action<Command> configure)
where IHandler : ICommandHandler<IRequest, IResponse> where THandler : ICommandHandler<TRequest, TResponse>
where IRequest : ICommandRequest<IResponse> where TRequest : ICommandRequest<TResponse>
where TResponse : class
{ {
var cmd = new Command(name, description) var cmd = new Command(name, description)
{ {
new Option<bool>("--json", "print result as json string"), new Option<bool>("--json", "print result as json string"),
new Option<bool>("--json-indented", "print result as indented json string") new Option<bool>("--json-indented", "print result as indented json string")
}; };
cmd.Handler = CommandHandler.Create((IRequest r, bool json, bool jsonIndented) => RunSubCommand<IHandler, IRequest, IResponse>(r, json, jsonIndented)); cmd.Handler = CommandHandler.Create((TRequest r, bool json, bool jsonIndented) => RunSubCommand<THandler, TRequest, TResponse>(r, json, jsonIndented));
configure(cmd); configure(cmd);
this.Add(cmd); this.Add(cmd);
} }
private static async Task<int> RunSubCommand<IHandler, IRequest, IResponse>(IRequest request, bool json, bool jsonIndented) private static async Task<int> RunSubCommand<THandler, TRequest, TResponse>(TRequest request, bool json, bool jsonIndented)
where IHandler : ICommandHandler<IRequest, IResponse> where THandler : ICommandHandler<TRequest, TResponse>
where IRequest : ICommandRequest<IResponse> where TRequest : ICommandRequest<TResponse>
where TResponse : class
{ {
var handler = Activator.CreateInstance<IHandler>(); var handler = Activator.CreateInstance<THandler>();
var response = await handler.Handle(request).ConfigureAwait(false); var response = await handler.Handle(request).ConfigureAwait(false);
@ -58,7 +60,16 @@ namespace BililiveRecorder.ToolBox
} }
else else
{ {
handler.PrintResponse(response); if (response.Status == ResponseStatus.OK)
{
handler.PrintResponse(response.Result!);
}
else
{
Console.Write("Error: ");
Console.WriteLine(response.Status);
Console.WriteLine(response.ErrorMessage);
}
} }
return 0; return 0;

View File

@ -1,21 +1,10 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.IO.Compression;
using System.IO.Pipelines;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using BililiveRecorder.Flv; using BililiveRecorder.ToolBox;
using BililiveRecorder.Flv.Grouping;
using BililiveRecorder.Flv.Parser;
using BililiveRecorder.Flv.Pipeline;
using BililiveRecorder.Flv.Writer;
using BililiveRecorder.Flv.Xml;
using BililiveRecorder.ToolBox.Commands; using BililiveRecorder.ToolBox.Commands;
using BililiveRecorder.WPF.Controls; using BililiveRecorder.WPF.Controls;
using BililiveRecorder.WPF.Models;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.WindowsAPICodePack.Dialogs; using Microsoft.WindowsAPICodePack.Dialogs;
using Serilog; using Serilog;
@ -50,7 +39,8 @@ namespace BililiveRecorder.WPF.Pages
NavigateToShortcut = true, NavigateToShortcut = true,
Filters = Filters =
{ {
new CommonFileDialogFilter("Flv files",".flv") new CommonFileDialogFilter("Flv files",".flv"),
new CommonFileDialogFilter("Flv Xml files",".xml,.gz")
} }
}; };
if (fileDialog.ShowDialog() == CommonFileDialogResult.Ok) if (fileDialog.ShowDialog() == CommonFileDialogResult.Ok)
@ -109,13 +99,25 @@ namespace BililiveRecorder.WPF.Pages
}); });
}).ConfigureAwait(true); }).ConfigureAwait(true);
if (resp.Status != ResponseStatus.OK)
{
logger.Warning(resp.Exception, "修复时发生错误 (@Status)", resp.Status);
await Task.Run(() =>
{
// TODO 翻译
// 例:在读取文件时发生了错误
// 选择的不是 FLV 文件
// FLV 文件格式错误
MessageBox.Show($"错误类型: {resp.Status}\n{resp.ErrorMessage}", "修复时发生错误", MessageBoxButton.OK, MessageBoxImage.Warning);
}).ConfigureAwait(true);
}
progressDialog.Hide(); progressDialog.Hide();
await showTask.ConfigureAwait(true); await showTask.ConfigureAwait(true);
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex, "修复时发生错误"); logger.Error(ex, "修复时发生未处理的错误");
MessageBox.Show("修复时发生错误\n" + ex.Message);
} }
finally finally
{ {
@ -156,15 +158,29 @@ namespace BililiveRecorder.WPF.Pages
}); });
}).ConfigureAwait(true); }).ConfigureAwait(true);
this.analyzeResultDisplayArea.DataContext = resp; if (resp.Status != ResponseStatus.OK)
{
logger.Warning(resp.Exception, "分析时发生错误 (@Status)", resp.Status);
await Task.Run(() =>
{
// TODO 翻译
// 例:在读取文件时发生了错误
// 选择的不是 FLV 文件
// FLV 文件格式错误
MessageBox.Show($"错误类型: {resp.Status}\n{resp.ErrorMessage}", "分析时发生错误", MessageBoxButton.OK, MessageBoxImage.Warning);
}).ConfigureAwait(true);
}
else
{
this.analyzeResultDisplayArea.DataContext = resp.Result;
}
progressDialog.Hide(); progressDialog.Hide();
await showTask.ConfigureAwait(true); await showTask.ConfigureAwait(true);
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex, "分析时发生错误"); logger.Error(ex, "分析时发生未处理的错误");
MessageBox.Show("分析时发生错误\n" + ex.Message);
} }
finally finally
{ {
@ -230,13 +246,25 @@ namespace BililiveRecorder.WPF.Pages
}); });
}).ConfigureAwait(true); }).ConfigureAwait(true);
if (resp.Status != ResponseStatus.OK)
{
logger.Warning(resp.Exception, "导出分析数据时发生错误 (@Status)", resp.Status);
await Task.Run(() =>
{
// TODO 翻译
// 例:在读取文件时发生了错误
// 选择的不是 FLV 文件
// FLV 文件格式错误
MessageBox.Show($"错误类型: {resp.Status}\n{resp.ErrorMessage}", "导出分析数据时发生错误", MessageBoxButton.OK, MessageBoxImage.Warning);
}).ConfigureAwait(true);
}
progressDialog.Hide(); progressDialog.Hide();
await showTask.ConfigureAwait(true); await showTask.ConfigureAwait(true);
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex, "导出时发生错误"); logger.Error(ex, "导出时发生未处理的错误");
MessageBox.Show("导出时发生错误\n" + ex.Message);
} }
finally finally
{ {