【问题标题】:Regex to parse image data URI正则表达式解析图像数据 URI
【发布时间】:2011-08-08 12:40:41
【问题描述】:

如果我有:

<img src="data:image/gif;base64,R0lGODlhtwBEANUAAMbIypOVmO7v76yusOHi49AsSDY1N2NkZvvs6VVWWPDAutZOWJ+hpPPPyeqmoNlcYXBxdNTV1nx+gN51c4iJjEdHSfbc19M+UOeZk7m7veSMiNtpauGBfu2zrc4RQSMfIP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAAAAAAALAAAAAC3AEQAAAb/QJBwSCwaj8ikcslsOp/QqHRKrVqv2Kx2y+16v+CweEwum8/otHrNbrvf8Lh8Tq/b7/i8fs" />

如何将数据部分解析为:

  • Mime 类型(图片/gif)
  • 编码(base64)
  • 图像数据(二进制数据)

【问题讨论】:

    标签: c# image base64 uri


    【解决方案1】:

    实际上,您不需要正则表达式。根据Wikipedia,数据URI格式为

    data:[<MIME-type>][;charset=<encoding>][;base64],<data>
    

    所以只需执行以下操作:

    byte[] imagedata = Convert.FromBase64String(imageSrc.Substring(imageSrc.IndexOf(",") + 1));
    

    【讨论】:

    • 感谢您的快速回复,但我也想知道 mime 类型,以便我可以将数据写入具有正确扩展名的文件中,如果用户提交 image/png,则为 .png , .gif 如果用户提交图像/gif 等。
    • 我认为这部分:imageSrc.IndexOf(",") 应该是 imageSrc.IndexOf(",")-1 以防止“,”包含在数据中。
    • 我可能会使用此方法删除数据部分,然后使用 Split(';') 获取其他部分。另外,认为它应该是 Indexof(",")+1,而不是 -1.. 当然,在实际代码中,您还需要检查 -1(未找到)结果。
    • @Steven 你是对的,但实际上是+1 而不是-1.. 编辑了我的答案
    【解决方案2】:

    编辑:展开以显示使用情况

    var regex = new Regex(@"data:(?<mime>[\w/\-\.]+);(?<encoding>\w+),(?<data>.*)", RegexOptions.Compiled);
    
    var match = regex.Match(input);
    
    var mime = match.Groups["mime"].Value;
    var encoding = match.Groups["encoding"].Value;
    var data = match.Groups["data"].Value;
    

    注意:正则表达式适用于有问题的输入。如果还指定了charset,它将不起作用,必须重写。

    【讨论】:

    • 我试过了,但在 c# 中使用matches.Groups[0].ToString() 它返回了所有内容而不是mime 部分。能否展开代码。
    • 这是我的代码:string pattern= @"data:(?[\w/]+);(?\w+),(?.*)" ;匹配匹配 = Regex.Match(imgsrc, pattern, RegexOptions.IgnoreCase | RegexOptions.Singleline); HttpContext.Current.Response.Write("
      match:" + matches.Groups[0].ToString());
    • 我稍微更新了正则表达式的 MIME 部分,以便能够与 application/vnd.openxmlformats-officedocument.spreadsheetml.sheet 等 MIME 类型一起使用,其中 \w 和 / 不足以匹配 (因为 . 和 - 不是“单词”字符)。
    • 不适用于数据:text/plain;charset=utf-8;base64,IVRSTlMJVFJ
    • 我知道它在问题中是明确的,但我发现通过获取逗号的位置然后处理媒体类型/编码部分,在没有正则表达式的情况下更容易解析。逻辑比复杂的正则表达式更清晰,我可以处理上面@YesMan85 这样的情况,我有点担心长数据字符串的正则表达式性能。
    【解决方案3】:

    这是我的正则表达式,我也必须将 mime 类型 (image/jpg) 分开。

    ^data:(?<mimeType>(?<mime>\w+)\/(?<extension>\w+));(?<encoding>\w+),(?<data>.*)
    

    【讨论】:

      【解决方案4】:

      数据 URI 对它们来说有点复杂,它们可以包含参数、媒体类型等……有时您需要知道这些信息,而不仅仅是数据。

      要解析数据 URI 并提取所有相关部分,请尝试以下操作:

      /**
       * Parse a data uri and return an object with information about the different parts
       * @param {*} data_uri 
       */
      function parseDataURI(data_uri) {
          let regex = /^\s*data:(?<media_type>(?<mime_type>[a-z\-]+\/[a-z\-\+]+)(?<params>(;[a-z\-]+\=[a-z\-]+)*))?(?<encoding>;base64)?,(?<data>[a-z0-9\!\$\&\'\,\(\)\*\+\,\;\=\-\.\_\~\:\@\/\?\%\s]*\s*)$/i;
          let result = regex.exec(data_uri);
          let info = {
              media_type: result.groups.media_type,
              mime_type: result.groups.mime_type,
              params: result.groups.params,
              encoding: result.groups.encoding,
              data: result.groups.data
          }
          if(info.params)
              info.params = Object.fromEntries(info.params.split(';').slice(1).map(param => param.split('=')));
          if(info.encoding)
              info.encoding = info.encoding.replace(';','');
          return info;
      }

      这将为您提供一个已解析出所有相关位的对象,并将参数作为字典 {foo: baz}。

      示例(带有断言的 mocha 测试):

      describe("Parse data URI", () => {
          it("Should extract data URI parts correctly",
              async ()=> {
                  let uri = 'data:text/vnd-example+xyz;foo=bar;bar=baz;base64,R0lGODdh';
                  let info = parseDataURI(uri);
                  assert.equal(info.media_type,'text/vnd-example+xyz;foo=bar;bar=baz');
                  assert.equal(info.mime_type,'text/vnd-example+xyz');
                  assert.equal(info.encoding, 'base64');
                  assert.equal(info.data, 'R0lGODdh');
                  assert.equal(info.params.foo, 'bar');
                  assert.equal(info.params.bar, 'baz');
              }
          );
      });

      【讨论】:

      • 正则表达式的最后一部分捕获尾随空格,因此您的 info.data 可以以空格和制表符等结尾。将最后一个括号移到 \s 之前,例如:regex = /^\sdata:(?(?[az\-]+\/[az\- \+]+)(?(;[az\-]+\=[az\-]+)))?(?;base64)?,(?[ a-z0-9\!\$\&\'\,()*\+\,\;\=\-\._\~\:\@\/\?\%\s])*\s *$/i;
      【解决方案5】:

      我还需要解析数据 URI 方案。因此,我专门针对 C# 改进了此页面上给出的正则表达式,它适合任何数据 URI 方案(要检查该方案,您可以从 herehere 获取它。

      这是我的 C# 解决方案:

      private class DataUriModel {
        public string MediaType { get; set; }
        public string Type { get; set; }
        public string[] Tree { get; set; }
        public string Subtype { get; set; }
        public string Suffix { get; set; }
        public string[] Params { get; set; }
        public string Encoding { get; set; }
        public string Data { get; set; }
      }
      
      static void Main(string[] args) {
        string s = "data:image/prs.jpeg+gzip;charset=UTF-8;page=21;page=22;base64,/9j/4AAQSkZJRgABAQAAAQABAAD";
        var parsedUri = GetDataURI(s);
        Console.WriteLine(decodedUri.Type);
        Console.WriteLine(decodedUri.Subtype);
        Console.WriteLine(decodedUri.Encoding);
      }
      
      private static DataUriModel GetDataURI(string data) {
        var result = new DataUriModel();
        Regex regex = new Regex(@"^\s*data:(?<media_type>(?<type>[a-z\-]+){1}\/(?<tree>([a-z\-]+\.)+)?(?<subtype>[a-z\-]+){1}(?<suffix>\+[a-z]+)?(?<params>(;[a-z\-]+\=[a-z0-9\-\+]+)*)?)?(?<encoding>;base64)?(?<data>,+[a-z0-9\\\!\$\&\'\,\(\)\*\+\,\;\=\-\.\~\:\@\/\?\%\s]*\s*)?$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Multiline);
        var match = regex.Match(data);
      
        if (!match.Success)
          return result;
      
        var names = regex.GetGroupNames();
        foreach (var name in names) {
          var group = match.Groups[name];
          switch (name) {
            case "media_type": result.MediaType = group.Value; break;
            case "type": result.Type = group.Value; break;
            case "tree": result.Tree = !string.IsNullOrWhiteSpace(group.Value) && group.Value.Length > 1 ? group.Value[0..^1].Split(".") : null; break;
            case "subtype": result.Subtype = group.Value; break;
            case "suffix": result.Suffix = !string.IsNullOrWhiteSpace(group.Value) && group.Value.Length > 1 ? group.Value[1..] : null; break;
            case "params": result.Params = !string.IsNullOrWhiteSpace(group.Value) && group.Value.Length > 1 ? group.Value[1..].Split(";") : null; break;
            case "encoding": result.Encoding = !string.IsNullOrWhiteSpace(group.Value) && group.Value.Length > 1 ? group.Value[1..] : null; break;
            case "data": result.Data = !string.IsNullOrWhiteSpace(group.Value) && group.Value.Length > 1 ? group.Value[1..] : null; break;
          }
        }
      
        return result;
      }
      

      【讨论】:

      • 谢谢,这对我有用。这是我第一次遇到 C# 范围运算符,因为我使用的是旧版本的 C#,所以我不得不将 Range Operator 的用法替换为对 Substring() 的调用。例如而不是group.Value[1..],我使用group.Value.Substring(1)
      猜你喜欢
      • 2015-07-08
      • 1970-01-01
      • 2011-08-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-24
      相关资源
      最近更新 更多