liuwenliang的个人博客分享 http://blog.sciencenet.cn/u/liuwenliang

博文

[转载]ArcGIS10.2服务切片 JPG/PNG 转换为 Bundle/Bundlx

已有 2095 次阅读 2021-11-17 19:11 |个人分类:arcgis server|系统分类:科研笔记|文章来源:转载

本文编写主要参考博客:


http://blog.csdn.net/abc553226713/article/details/8668839




Bundle文件与Bundlx文件内容分析


Bundle文件:


4字节存储长度 + 图片Byte[]循环存储


最大存储16384(128*128)这样的循环


可以看出参考文章中Bundle文件只存图片数据


*更正*


参考文章没写完全,10.2的Bundle前面有60个字节是描述性文字


已尝试将其全部置0,ArcServer依然可以正确显示图片


正确格式是:


60字节描述头 + (4字节存储长度 + 图片Byte[] 循环存储)



Bundlx文件:


内容结构 16byte +81920byte + 16byte


其中2个16字节 文件头信息与尾信息 属于描述性注记 内容无用


经实践证明故意修改ArcServer自带创建的Bundlx文件将前后16位描述性注记写为空的时候ArcServer也不会去理会,依然可以正确读取数据


 


中间的81920字节文件主体信息 内容包含:


81920 = 5*128*128   5字节存储偏移量 有128*128个5字节


所以每个Bundle文件最多存储128*128 = 16384个子文件


另外要注意这个5字节对应的JPG文件是按列排序的不是按行



注意:由于Bundle文件在文件头有60字节的描述,所以Bundlx文件默认第一个的偏移量一定是60



Bundlx文件偏移量解释


偏移量是描述一个图片在Bundle文件中的起点,偏移量由5位Buffer转Int数值,读取偏移量参考代码:


long offset =(long)(buffer[0]& 0xff) + (long)(buffer[1] & 0xff)


                       *256 + (long)(buffer[2]& 0xff) * 65536


                      + (long)(buffer[3] &0xff) * 16777216


                       + (long)(buffer[4]& 0xff) * 4294967296L;


这个表示方法表示偏移量是由低位往高位进行表示的


比如byte[5]{0,1,0,0,0}= 偏移 1 * 256 length


又如byte[5]{0,1,5,0,0}= 偏移 1 * 256 + 5 * 256 * 256 length


每一位表示 位值 * 256的位数开方将所有位相加得到位值


同理Bundle文件的4字节存储长度也是这个表达方式


实质上就是一个256进制与10进制的转换过程(因为一个Byte只能存256个不同的值,最大化利用)



Bundle/Bundlx文件命名规则解释


R+4位16进制数字+C+4位16进制数字组成


比如R0080C0180表示这个紧凑格式表达了


第129-256行384-512列


行列顺序从指定的切片原点算(这个与普通切片相同)


 


现在对同一范围同比例尺进行ArcServer紧凑切片与切片工具切片


发现Jpg起点与Bundle起点相差了一些


R2380C1280 转十进制表示 切片范围左上角起点:


9088行 4736列


R23cdC12b6 转十进制表示 切片范围左上角起点:


9165行 4790 列


出现差异是因为Jpg是由原点以Step为1递增到实际位置,实际范围的起点位置更为精确


而Bundle是由原点以Step为128递增到实际位置(行列数总是128的倍数)


 


所以要解决这个问题,必须重算更新的Jpg文件在所归属的Bundle文件范围,并将这些文件注册到这些范围中,类似注册空间索引,然后根据这些注册信息,将数据写入Bundle



如何实现切片成果JPG转Bundle功能


第一步 获取更新图片JPG文件计算每个文件的十进制行列


第二步 获取切片原点 计算包含此更新区域的Bundle文件命名 以及十进制的行列范围


第三步 写Bundle文件


3.1匹配每个JPG文件到Bundle范围中


3.2写入Jpg的大小并转高低位保存在头4个Byte中(注意,如果图片内容为空,依然要填写4个0000补齐位数)


3.3写入Byte[] 内容到Bundle文件中


3.4记录每个文件的行列号和并记录每个文件的序号与长度


第四步 写Bundlx文件


根据写Bundle产生的信息,依次按列填写每个文件的偏移量



这里个功能不仅可以从JPG转换到Bundle 而且还可以对已有Bundle进行JPG更新



★转换全代码★


使用 只需要指定 JPG的Path 与目标Bundle文件的Path 即可 调用样例代码


        static void Main(string[] args)

        {

            PackageBundle BundleMaker = new PackageBundle();

            BundleMaker._strJPGPath = @"C:\JPGList";

            BundleMaker._strBundlePath = @"C:\_alllayers0";

            BundleMaker.StartPackage();

        }


class PackageBundle

    {

        public string _strJPGPath;

        public string _strBundlePath;



        private CommonTools _tool = new CommonTools();

        public void StartPackage()

        {

            try

            {

                if (!new DirectoryInfo(_strBundlePath).Exists)

                    new DirectoryInfo(_strBundlePath).Create();

                //读取一个Jpg文件夹,并将其组合为Bundle/Bundlx文件



                //第一步 依次遍历每个L00 L01 级别

                DirectoryInfo RootDir = new DirectoryInfo(_strJPGPath);//C:\Users\chop\Desktop\_alllayers

                DirectoryInfo[] LevelDirList = RootDir.GetDirectories();

                foreach (DirectoryInfo LevelDir in LevelDirList)

                {

                    int MinRow,MinColumn,MaxRow,MaxColumn;

                    //获取JPG内容的范围

                    GetJpgNamingEnv(LevelDir.FullName,out MinRow,out MinColumn,out MaxRow,out MaxColumn);//C:\Users\chop\Desktop\_alllayers\L00

                    //获取Bundle文件

                    List<Bundle> BundleList = CreateBundleList(_strBundlePath + "\\" + LevelDir.Name, MinRow, MinColumn, MaxRow, MaxColumn);

                    //匹配JPG文件到Bundle文件

                    AttachJPGToBundle(LevelDir.FullName,BundleList);

                    //开始构造Bundle Bundlx文件

                    CreateBundleFiles(BundleList);

                }

            }

            catch (Exception ex)

            {

                Console.WriteLine(ex.Message);

            }

            finally

            {

                Console.WriteLine("打包完毕");

                Console.ReadKey();

            }

        }

 

        private void GetJpgNamingEnv(string strJPGLevelPath,out int MinRow,out int MinColumn,out int MaxRow,out int MaxColumn)

        {

            MinRow = 0; MinColumn = 0; MaxRow = 0; MaxColumn = 0;



            //获取所有文件夹 他们代表着行

            DirectoryInfo[] RowDirList = _tool.GetAllDirectory(strJPGLevelPath);

            //获取所有文件 他们代表着列

            FileInfo[] ColumnFileList = _tool.GetAllFiles(strJPGLevelPath);



            //翻译每一个文件夹的名称(行)到 十进制 并且排序

            List<int> RowList = new List<int>();

            foreach (DirectoryInfo RowDir in RowDirList)

            {

                string Num16 = RowDir.Name.TrimStart('R');

                string Num10 = _tool.To10(Num16);

                RowList.Add(int.Parse(Num10));

            }

            RowList.Sort();

            MinRow = RowList[0];

            MaxRow = RowList[RowList.Count - 1];



            List<int> ColumnList = new List<int>();

            foreach (FileInfo Fileinfo in ColumnFileList)

            {

                string Num16 = Fileinfo.Name.TrimStart('C').TrimEnd(Fileinfo.Extension.ToCharArray());

                string Num10 = _tool.To10(Num16);

                ColumnList.Add(int.Parse(Num10));

            }

            ColumnList.Sort();

            MinColumn = ColumnList[0];

            MaxColumn = ColumnList[ColumnList.Count - 1];

        }



        class Bundle

        {

            public string strFileName;

            public int MinRow;

            public int MinColumn;

            public int MaxRow;

            public int MaxColumn;

            public Bundle()

            {

                JPGList = new List<FileInfo>();

                JPGIDList = new List<string>();

            }

            public List<FileInfo> JPGList;

            public List<string> JPGIDList;

        }



        //根据JPG文件最大最小的行列 创建Bundle集合

        private List<Bundle> CreateBundleList(string strBundleLevelDir,int MinRow, int MinColumn, int MaxRow, int MaxColumn)

        {

            //Bundle文件是行列为128个小JPG组成的 每行每列以128为Step从原点递增



            //这里获取一个范围的Bundle 需要从最小的行列 开始计算起始Bundle



            //然后由起始Bundle递增128 来增加Bundle数量 只要递增的Bundle的起始行列 始终在MaxRow MaxColumn范围内 那么这个Bundle就是有效Bundle

            int Size = 128;

            int BundleRow = (MinRow / Size) * Size;

            int BundleColumn = (MinColumn / Size) * Size;



            //这里建立起来的Bundle有可能是无效Bundle(因为更新区域不可能是规则的矩形),即不包含任何Jpg的Bundle,先不管,到后面判定时再删除这些Bundle

            List<Bundle> BundleList = new List<Bundle>();

            int BundleColumnMin = BundleColumn;



            if (!new DirectoryInfo(strBundleLevelDir).Exists)

                new DirectoryInfo(strBundleLevelDir).Create();



            while(BundleRow < MaxRow)

            {

                while(BundleColumn < MaxColumn)

                {

                    Bundle bundle = new Bundle();

                    bundle.MinRow = BundleRow;

                    bundle.MaxRow = BundleRow + Size;

                    bundle.MinColumn = BundleColumn;

                    bundle.MaxColumn = BundleColumn + Size;



                    bundle.strFileName = strBundleLevelDir + "\\" + "R" + _tool.To16(bundle.MinRow.ToString()) + "C" + _tool.To16(bundle.MinColumn.ToString()) + ".bundle";



                    BundleList.Add(bundle);

                    BundleColumn = BundleColumn + Size;

                }

                BundleColumn = BundleColumnMin;

                BundleRow = BundleRow + Size;

            }

            return BundleList;

        }



        //将每个JPG归属到Bundle中

        private void AttachJPGToBundle(string strJPGLevelPath, List<Bundle> BundleList)

        {

            //获取所有文件

            FileInfo[] ColumnFileList = _tool.GetAllFiles(strJPGLevelPath);



            //获取每个文件所归属的行列 用十进制表示

            foreach (FileInfo colFile in ColumnFileList)

            {

                int dColumnNum = int.Parse(_tool.To10(colFile.Name.Substring(5, 4)));

                int dRowNum = int.Parse(_tool.To10(colFile.DirectoryName.Substring(colFile.DirectoryName.Length - 4, 4)));

                foreach (Bundle bundleFile in BundleList)

                {

                    if ((bundleFile.MinRow <= dRowNum && dRowNum < bundleFile.MaxRow) && (bundleFile.MinColumn <= dColumnNum && dColumnNum < bundleFile.MaxColumn))

                    {

                        bundleFile.JPGList.Add(colFile);

                        bundleFile.JPGIDList.Add(dRowNum.ToString() + "-" + dColumnNum.ToString());

                    }

                }

            }



            //除去冗余Bundle

            for (int i = 0; i < BundleList.Count; i++)

            {

                if (BundleList[i].JPGList.Count == 0)

                {

                    BundleList.RemoveAt(i);

                    i--;

                }

            }

        }



        private void CreateBundleFiles(List<Bundle> BundleList)

        {

            //从第一列128个开始往下写,写到N列 每次写到算好对应的16进制文件名并组成路径与List已有对比

            foreach (Bundle bundleFile in BundleList)

            {

                //第一种情况Bundle不存在,这时需要从Bundle文件直接写入

                if (!new FileInfo(bundleFile.strFileName).Exists)

                {

                    WriteBundleFile(bundleFile);

                }

                //第二种情况Bundle已经存在 这时需要从Bundlx文件开始分析写入

                else

                {

                    RewriteBundleFile(bundleFile);

                }

            }

        }



        private void WriteBundleFile(Bundle bundleFile)

        {

            FileStream FsBundle = new FileStream(bundleFile.strFileName, FileMode.CreateNew, FileAccess.Write);

            FileStream FsBundlx = new FileStream(bundleFile.strFileName.TrimEnd('e') + "x", FileMode.CreateNew, FileAccess.Write);

            FsBundle.Seek(0, SeekOrigin.Begin);//前60个描述内容统一置0

            FsBundlx.Seek(0, SeekOrigin.Begin);//前16个描述内容统一置0 



            byte[] emptyBytes = new byte[60];

            for (int ie = 0; ie < 60; ie++)

                emptyBytes[ie] = 88;

            FsBundle.Write(emptyBytes, 0, 60);

            FsBundlx.Write(emptyBytes, 0, 16);//Bundlx的起点是60 因为Bundle前60个是描述Byte



            int dOffset = 60;

            for (int i = 0; i < 128; i++)

            {

                for (int j = 0; j < 128; j++)

                {

                    string strCurrentID = (bundleFile.MinRow + j).ToString() + "-" + (bundleFile.MinColumn + i).ToString();

                    int dJPGIndex = bundleFile.JPGIDList.IndexOf(strCurrentID);

                    //写空白图片

                    if (dJPGIndex == -1)

                    {

                        FsBundle.Write(OffsetToByte4(0), 0, 4);



                        FsBundlx.Write(OffsetToByte5(dOffset), 0, 5);



                        dOffset = dOffset + 4;

                    }

                    //写实际图片

                    else

                    {

                        byte[] JPGBytes = ReadJPGByte(bundleFile.JPGList[dJPGIndex].FullName);



                        FsBundle.Write(OffsetToByte4(JPGBytes.Length), 0, 4);



                        FsBundle.Write(JPGBytes, 0, JPGBytes.Length);



                        FsBundlx.Write(OffsetToByte5(dOffset), 0, 5);



                        dOffset = dOffset + 4 + JPGBytes.Length;

                    }

                }

            }

            FsBundlx.Write(emptyBytes, 0, 16);//别忘了添加后16个描述内容 后2个描述内容统一置0 

            FsBundle.Close();

            FsBundlx.Close();

        }



        private void RewriteBundleFile(Bundle bundleFile)

        {

            FileStream FsBundle = new FileStream(bundleFile.strFileName + ".Buffer", FileMode.CreateNew, FileAccess.Write);

            FileStream FsBundlx = new FileStream(bundleFile.strFileName.TrimEnd('e') + "x" + ".Buffer", FileMode.CreateNew, FileAccess.Write);



            FileStream FsBundleSource = new FileStream(bundleFile.strFileName, FileMode.Open, FileAccess.Read);

            FileStream FsBundlxSource = new FileStream(bundleFile.strFileName.TrimEnd('e') + "x", FileMode.Open, FileAccess.Read);



            FsBundle.Seek(0, SeekOrigin.Begin);//前60个描述内容统一置0

            FsBundlx.Seek(0, SeekOrigin.Begin);//前16个描述内容统一置0 



            FsBundleSource.Seek(0, SeekOrigin.Begin);

            FsBundlxSource.Seek(0, SeekOrigin.Begin);



            byte[] emptyBytes = new byte[60];

            for (int ie = 0; ie < 60; ie++)

                emptyBytes[ie] = 88;

            FsBundle.Write(emptyBytes, 0, 60);

            FsBundlx.Write(emptyBytes, 0, 16);//Bundlx的起点是60 因为Bundle前60个是描述Byte



            int dOffset = 60;

            for (int i = 0; i < 128; i++)//column

            {

                for (int j = 0; j < 128; j++)//row

                {

                    string strCurrentID = (bundleFile.MinRow + j).ToString() + "-" + (bundleFile.MinColumn + i).ToString();

                    int dJPGIndex = bundleFile.JPGIDList.IndexOf(strCurrentID);

                    //写已有Bundle内容图片

                    if (dJPGIndex == -1)

                    {

                        byte[] JPGBytes = GetBundleData(FsBundleSource, FsBundlxSource, j, i);



                        if (JPGBytes.Length == 0)

                        {

                            FsBundle.Write(OffsetToByte4(0), 0, 4);



                            FsBundlx.Write(OffsetToByte5(dOffset), 0, 5);



                            dOffset = dOffset + 4;

                        }

                        else

                        {

                            FsBundle.Write(OffsetToByte4(JPGBytes.Length), 0, 4);



                            FsBundle.Write(JPGBytes, 0, JPGBytes.Length);



                            FsBundlx.Write(OffsetToByte5(dOffset), 0, 5);



                            dOffset = dOffset + 4 + JPGBytes.Length;

                        }

                    }

                    //写实际图片

                    else

                    {

                        byte[] JPGBytes = ReadJPGByte(bundleFile.JPGList[dJPGIndex].FullName);



                        FsBundle.Write(OffsetToByte4(JPGBytes.Length), 0, 4);



                        FsBundle.Write(JPGBytes, 0, JPGBytes.Length);



                        FsBundlx.Write(OffsetToByte5(dOffset), 0, 5);



                        dOffset = dOffset + 4 + JPGBytes.Length;

                    }

                }

            }

            FsBundlx.Write(emptyBytes, 0, 16);//别忘了添加后16个描述内容 后2个描述内容统一置0 

            FsBundle.Close();

            FsBundlx.Close();

            FsBundleSource.Close();

            FsBundlxSource.Close();



            //全部写入完毕后 还需要将Buffer文件替换到实际命名的文件;

            new FileInfo(bundleFile.strFileName).Delete();

            new FileInfo(bundleFile.strFileName.TrimEnd('e') + "x").Delete();

            new FileInfo(bundleFile.strFileName + ".Buffer").MoveTo(bundleFile.strFileName);

            new FileInfo(bundleFile.strFileName.TrimEnd('e') + "x" + ".Buffer").MoveTo(bundleFile.strFileName.TrimEnd('e') + "x"); ;

        }



        private byte[] GetBundleData(FileStream FsBundleSource, FileStream FsBundlxSource,int dRow,int dColomn)

        {

            int dIndex = dColomn * 128 + dRow;

            FsBundlxSource.Seek(16 + 5 * dIndex, SeekOrigin.Begin);

            byte[] buffer = new byte[5];

            FsBundlxSource.Read(buffer, 0, 5);

            long offset = (long)(buffer[0] & 0xff) + (long)(buffer[1] & 0xff)

                    * 256 + (long)(buffer[2] & 0xff) * 65536

                   + (long)(buffer[3] & 0xff) * 16777216

                    + (long)(buffer[4] & 0xff) * 4294967296L;



            FsBundleSource.Seek(offset, SeekOrigin.Begin);

            byte[] lengthBytes = new byte[4];

            FsBundleSource.Read(lengthBytes, 0, 4);

            int length = (int)(lengthBytes[0] & 0xff)

                    + (int)(lengthBytes[1] & 0xff) * 256

                    + (int)(lengthBytes[2] & 0xff) * 65536

                    + (int)(lengthBytes[3] & 0xff) * 16777216;

            byte[] result = new byte[length];

            FsBundleSource.Read(result, 0, length);

            return result;

        }



        //10进制转256进制 4位表示法

        private byte[] OffsetToByte4(int dOffset)

        {

            return System.BitConverter.GetBytes(dOffset);

        }



        //10进制转256进制 5位表示法

        private byte[] OffsetToByte5(long dOffset)

        {

            byte[] byte8 = System.BitConverter.GetBytes(dOffset);

            byte[] byte5 = new byte[] { byte8[0], byte8[1], byte8[2], byte8[3], byte8[4] };

            return byte5;

        }



        //读取文件字节流到Byte[]

        private byte[] ReadJPGByte(string strJPGPath)

        {

            using (FileStream fsRead = new FileStream(strJPGPath, FileMode.Open, FileAccess.Read))

            {

                byte[] Buffer = new byte[fsRead.Length];

                fsRead.Read(Buffer, 0, Buffer.Length);

                return Buffer;

            }

        }

    }



淡墨青山

关注


————————————————

版权声明:本文为CSDN博主「淡墨青山」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/x15926292599/article/details/78794662




https://blog.sciencenet.cn/blog-3409972-1312821.html

上一篇:[转载]arcgis server 紧促(bundle)格式缓存文件的读取
下一篇:[转载]C# byte数组与Image的相互转换
收藏 IP: 210.72.26.*| 热度|

0

该博文允许注册用户评论 请点击登录 评论 (0 个评论)

数据加载中...
扫一扫,分享此博文

Archiver|手机版|科学网 ( 京ICP备07017567号-12 )

GMT+8, 2024-10-20 13:32

Powered by ScienceNet.cn

Copyright © 2007- 中国科学报社

返回顶部