4 Commits

  1. 2
      Apewer/Apewer.props
  2. 99
      Apewer/DrawingUtility.cs
  3. 28
      Apewer/StorageUtility.cs
  4. 39
      Apewer/Web/ApiContext.cs
  5. 12
      ChangeLog.md

2
Apewer/Apewer.props

@ -7,7 +7,7 @@
<Description></Description>
<RootNamespace>Apewer</RootNamespace>
<Product>Apewer Libraries</Product>
<Version>6.8.9</Version>
<Version>6.8.10</Version>
</PropertyGroup>
<!-- 生成 -->

99
Apewer/DrawingUtility.cs

@ -25,6 +25,72 @@ namespace Apewer
return graphics;
}
/// <summary>识别图像的旋转角度并进行调整。</summary>
/// <exception cref="ArgumentNullException"></exception>
public static void AdjustRotation(this Image image)
{
if (image == null) throw new ArgumentNullException(nameof(image));
// 检测旋转角度
var angle = 0;
if (Array.IndexOf(image.PropertyIdList, 274) > -1)
{
var property = image.GetPropertyItem(274);
var value = property.Value[0];
switch (value)
{
case 1: break;
case 3: angle = 180; break;
case 6: angle = 90; break;
case 8: angle = 270; break;
}
}
// 自适应旋转
switch (angle)
{
case 90: image.RotateFlip(RotateFlipType.Rotate90FlipNone); break;
case 180: image.RotateFlip(RotateFlipType.Rotate180FlipNone); break;
case 270: image.RotateFlip(RotateFlipType.Rotate270FlipNone); break;
}
}
/// <summary>作为图像,执行回调。</summary>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
public static void AsImage(this byte[] imageData, Action<Image> callback)
{
if (imageData == null) throw new ArgumentNullException(nameof(imageData));
if (callback == null) throw new ArgumentNullException(nameof(callback));
using (var stream = new MemoryStream(imageData))
{
using (var image = Image.FromStream(stream))
{
AdjustRotation(image);
callback.Invoke(image);
}
}
}
/// <summary>作为图像,执行回调。</summary>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
public static TResult AsImage<TResult>(this byte[] imageData, Func<Image, TResult> callback)
{
if (imageData == null) throw new ArgumentNullException(nameof(imageData));
if (callback == null) throw new ArgumentNullException(nameof(callback));
using (var stream = new MemoryStream(imageData))
{
using (var image = Image.FromStream(stream))
{
AdjustRotation(image);
return callback.Invoke(image);
}
}
}
/// <summary>保存图像为文件。</summary>
/// <exception cref="ArgumentNullException"></exception>
public static byte[] Save(this Image image, ImageFormat format)
@ -63,6 +129,38 @@ namespace Apewer
}
}
/// <summary>等比例缩放,生成新图像。</summary>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static Image AspectScale(this Image origin, int maxSide, Color? backgroundColor = null) => AspectScale(origin, maxSide, maxSide, backgroundColor);
/// <summary>等比例缩放,生成新图像。</summary>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static Image AspectScale(this Image origin, int maxWidth, int maxHeight, Color? backgroundColor = null)
{
if (origin == null) throw new ArgumentNullException(nameof(origin));
if (maxWidth < 1) throw new ArgumentOutOfRangeException(nameof(maxWidth));
if (maxHeight < 1) throw new ArgumentOutOfRangeException(nameof(maxHeight));
var width = origin.Width;
var height = origin.Height;
var radio = Convert.ToDouble(width) / Convert.ToDouble(height);
if (width > maxWidth)
{
width = maxWidth;
height = Convert.ToInt32(maxWidth / radio);
}
if (height > maxHeight)
{
width = Convert.ToInt32(maxHeight * radio);
height = maxHeight;
}
return Scale(origin, width, height, backgroundColor);
}
/// <summary>缩放图像,生成新图像。</summary>
/// <param name="image">原始图像。</param>
/// <param name="size">新图像的大小。</param>
@ -102,6 +200,7 @@ namespace Apewer
if (isTransparentFormat)
{
if (backgroundColor != null && backgroundColor.HasValue) graphic.Clear(backgroundColor.Value);
else graphic.Clear(Color.Transparent);
}
// 绘制新图像

28
Apewer/StorageUtility.cs

@ -311,7 +311,7 @@ namespace Apewer
#region file
/// <summary>打开文件,并获取文件流。若文件不存在,则先创建文件;若获取失败,则返回 NULL 值。可选文件的锁定状态。</summary>
/// <summary>打开文件,并获取文件流。若文件不存在,则先创建文件。</summary>
public static FileStream OpenFile(string path, bool share = true)
{
try
@ -450,27 +450,19 @@ namespace Apewer
}
else
{
var file = OpenFile(path, true);
var success = false;
try
using (var file = OpenFile(path, true))
{
var dir = Path.GetDirectoryName(path);
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
var write1 = BytesUtility.Write(file, TextUtility.Bom);
if (write1 == TextUtility.Bom.Length)
try
{
var write2 = BytesUtility.Write(file, bytes);
if (bytes != null && bytes.LongLength > 0L)
{
}
success = true;
file.SetLength(0);
BytesUtility.Write(file, TextUtility.Bom);
BytesUtility.Write(file, bytes);
return true;
}
catch {
return false;
}
}
catch { }
RuntimeUtility.Dispose(file);
return success;
}
}
}

39
Apewer/Web/ApiContext.cs

@ -1,9 +1,6 @@
using Apewer.Network;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System;
using System.Reflection;
using System.Text;
using System.Threading;
namespace Apewer.Web
{
@ -69,6 +66,36 @@ namespace Apewer.Web
#endregion
#region Current
#if NET461_OR_GREATER || NETSTANDARD || NETCOREAPP
static AsyncLocal<ApiContext> _current = new AsyncLocal<ApiContext>();
#endif
static void SetCurrent(ApiContext context)
{
#if NET461_OR_GREATER || NETSTANDARD || NETCOREAPP
_current.Value = context;
#else
System.Runtime.Remoting.Messaging.CallContext.LogicalSetData("ApiContext", context);
#endif
}
static ApiContext GetCurrent()
{
#if NET461_OR_GREATER || NETSTANDARD || NETCOREAPP
if (_current == null) return null;
return _current.Value;
#else
return System.Runtime.Remoting.Messaging.CallContext.LogicalGetData("ApiContext") as ApiContext;
#endif
}
/// <summary>获取当前的 <see cref="ApiContext" /> 实例。</summary>
public static ApiContext Current { get => GetCurrent(); }
#endregion
internal ApiContext(ApiInvoker invoker, ApiProvider provider, ApiEntries entries)
{
if (invoker == null) throw new ArgumentNullException(nameof(invoker));
@ -78,9 +105,9 @@ namespace Apewer.Web
_invoker = invoker;
_provider = provider;
_entries = entries;
_options = invoker.Options ?? new ApiOptions();
SetCurrent(this);
}
/// <summary>自定义数据。若此自定义数据实现了 <see cref="IDisposable" />,将会与 Context 一起自动释放。</summary>

12
ChangeLog.md

@ -1,4 +1,14 @@
### 6.8.9
### 6.8.10
- 新特性
- 增加 ApiContext.Current 属性,用于获取当前的 API 上下文;
- 增加 Query.Column<T> 和 Query.Cell<T> 方法,支持指定输出类型;
- 增加 DrawingUtility.AsImage 方法,将字节数组作为 Image 执行回调;
- 增加 DrawingUtility.AdjustRotation 方法,自动调整图片的旋转方向;
- 增加 DrawingUtility.AspectScale 方法,实现等比例缩放,用于生成缩略图。
- 问题修正
- 修正 WriteFile 在带有 BOM 时不清空文件的问题。
### 6.8.9
- 新特性
- 增加扩展方法 Stream.Read(int),用于读取指定长度的字节数组;
- TextUtility 增加 Pascal、Kebab 和 Snake 方法,用于转换字符串的命名风格;

Loading…
Cancel
Save