在C#中将byte[]拆分为多个byte[]数组

15

我正在尝试对图像的字节进行“切块(chunk)”。这将允许我分部上传大型图像。目前,我已将图像存储为一个大的byte[]数组。我想将该字节数组拆分为长度不超过512个元素的byte[]数组。然而,我不确定最有效的方法是什么。

有没有人知道如何以最有效的方式实现这一点?


1
请查看此链接:https://dev59.com/nHVD5IYBdhLWcg3wTZ1m希望能帮到你, 再见 - Mitja Bonca
1
看起来很容易想出一个O(N)的解决方案(通过一次迭代遍历所有字节)。你是在寻找比O(N)更好的解决方案吗? - mbeckish
类似问题 http://stackoverflow.com/questions/11765336/how-to-divide-array-of-file-paths-into-several-smaller-arrays/11765423#11765423 - GSerjo
5个回答

16

我为此编写了一个扩展,最初是针对字符串的,但后来决定将其通用化。

    public static T[] CopySlice<T>(this T[] source, int index, int length, bool padToLength = false)
    {
        int n = length;
        T[] slice = null;

        if (source.Length < index + length)
        {
            n = source.Length - index;
            if (padToLength)
            {
                slice = new T[length];
            }
        }

        if(slice == null) slice = new T[n];
        Array.Copy(source, index, slice, 0, n);
        return slice;
    }

    public static IEnumerable<T[]> Slices<T>(this T[] source, int count, bool padToLength = false)
    {
        for (var i = 0; i < source.Length; i += count)
            yield return source.CopySlice(i, count, padToLength);
    }

基本上,您可以这样使用它:
byte[] myBytes; // original byte array

foreach(byte[] copySlice in myBytes.Slices(10))
{
    // do something with each slice
}

编辑:我在 Stack Overflow 上提供了一个使用 Buffer.BlockCopy 的答案(这里),但是 BlockCopy 只能用于 byte[] 数组,因此无法为字符串创建通用版本。


不确定这个名称是否选择得当。通常,数组切片是指原始数组的一部分。(类似于ArraySegment<T>的语义。) - CodesInChaos
@CodesInChaos - 我愿意接受新的名称... 你能提供一个建议吗? - Chris Gessler
1
“CopySlice” 相当明确。或者类似于 “Substring” 的 “Subarray”。给函数命名真是太难了… - CodesInChaos
@CodesInChaos - 很好。谢谢。+1 给你 :) - Chris Gessler
无法适用于 byte[] myBytes = new byte[33]; foreach (byte[] copySlice in myBytes.Slices(16)) {....} - L.B
@L.B - 抱歉...我去拿了我的生产版本,它添加了代码来填充最终的数组,或者不填充并且可以处理奇数(剩余部分)。 - Chris Gessler

7
最有效的方法是:不要使用分块。如果您已经将图像作为单个byte[],则对于本地代码,仅指定偏移量和长度(例如某个byte的ArraySegment)通常就足够了。如果您的上传API仅接受byte[],那么您仍然不应将其完全分块;只需使用一个512缓冲区,并使用Buffer.BlockCopy将其加载为连续的数据片段。您可能需要调整(Array.Resize)最终块的大小,但最多只需要2个数组。
更好的方法是:避免首先需要byte[]:考虑通过流式API加载数据(如果数据来自文件,则这将很好地工作);只需使用Read(在循环中处理返回值等)来填充最大512个字节的数据块。例如(未经测试,仅供说明):
byte[] buffer = new byte[512];
while(true) {
    int space = 512, read, offset = 0;
    while(space > 0 && (read = stream.Read(buffer, offset, space)) > 0) {
        space -= read;
        offset += read;
    }
    // either a full buffer, or EOF
    if(space != 0) { // EOF - final
       if(offset != 0) { // something to send
         Array.Resize(red buffer, offset);
         Upload(buffer);
       }
       break;
    } else { // full buffer
       Upload(buffer);
    }
}

4
public static IEnumerable<byte[]> Split(this byte[] value,int bufferLength){
   int countOfArray = value.Length / bufferLength;
   if(value.Length % bufferLength > 0)
      countOfArray ++;
   for(int i=0;i<countOfArray;i++)
   {
      yield return value.Skip(i * bufferLength).Take(bufferLength).ToArray();

   }
}

这是我使用的扩展程序


2
我知道这已经过时了,但我需要同样的解决方案,以下内容对我非常有效,希望能帮到其他人。"最初的回答"
private byte[][] ByteArrayToChunks(byte[] byteData, long BufferSize)
{
    byte[][] chunks = byteData.Select((value, index) => new { PairNum = Math.Floor(index / (double)BufferSize), value }).GroupBy(pair => pair.PairNum).Select(grp => grp.Select(g => g.value).ToArray()).ToArray();
    return chunks;
}

看起来很不错,但并不是很高效。尽管如此,还是要给这个替代方案点赞。 - Stefan

2
从.NET 6开始,System.Linq内置了一个用于此目的的Chunk方法。
例如:
using System.Linq;

...
  byte[] myBytes = new byte[131072];
  int myMaxChunkSizeInBytes = 512;
  foreach(byte[] chunk in myBytes.Chunk(myMaxChunkSizeInBytes))
  {
      // do something with each chunk
  }
...

微软文档:System.Linq.Enumerable.Chunk(IEnumerable, Int32)



网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接