Недопустимое исключение символов при добавлении метаданных в CloudBlob

Task

Загрузите файл в Azure Blob Storage с исходным именем файла и также назначьте имя файла как meta-data в CloudBlob

Проблема

Эти символы недопустимы в meta-data, но приемлемы как имя blob:

š Š ñ Ñ ç Ç ÿ Ÿ ž Ž Ð œ Œ « » éèëêð ÉÈËÊ àâä ÀÁÂÃÄÅ àáâãäå ÙÚÛÜ ùúûüµ òóôõöø ÒÓÔÕÖØ ìíîï ÌÍÎÏ

Вопрос

  • Есть ли способ сохранить эти символы в meta-data? Не хватает ли некоторых параметров, вызывающих это исключение?
  • Большинство этих символов являются стандартными глифами на некоторых языках, поэтому как их обрабатывать?
  • Есть ли какая-либо документация, которая дает советы по этой проблеме? Я нашел соглашения об именах blob и meta-data, но ни о каких самих данных!

код

var dirtyFileName      = file.FileName;
var normalizedFileName = file.FileName.CleanOffDiacriticAndNonASCII();

// Blob name accepts almost characters that are acceptable as filenames in Windows
var blob = container.GetBlobReference(dirtyFileName);

//Upload content to the blob, which will create the blob if it does not already exist.
blob.Metadata["FileName"] = normalizedFileName;
blob.Attributes.Properties.ContentType = file.ContentType;

// ERROR: Occurs here!
blob.UploadFromStream(file.InputStream);

blob.SetMetadata();
blob.SetProperties();

Error

Исключение

Ссылки


Обходные

Незаконные символы в имени файла - это только верхушка льда, увеличенная только для цели этого вопроса! Большая картина состоит в том, что мы индексируем эти файлы с помощью Lucene.net, и поэтому для этого нужно много meta-data для хранения на blob. Пожалуйста, не предлагайте хранить все отдельно в базе данных, просто не делайте этого! До сих пор нам повезло, что мы встретили только один файл с диакритическими символами!

Итак, на данный момент мы прилагаем усилия, чтобы избежать сохранения имени файла в meta-data в качестве обходного пути!

Ответ 1

Просто было подтверждение от команды azure-sdk-for-net на GitHub, которая ASCII действительны как данные в пределах blob meta-data.

joeg прокомментировал:
Поддерживаемые символы в метаданных blob должны быть ASCII-символами. Работать это вы можете либо избежать строки (процент кодирования), base64 encode и т.д.

Источник на GitHub

Итак, как обход, либо:

  • удалите строку (процентное кодирование), base64 encode и т.д., как предложено joeg
  • используйте методы, которые я упомянул в своем другом ответе.

Ответ 2

Если я получаю ответ, который действительно решает проблему, это решение является решением для вышеупомянутой проблемы!

Обход

Чтобы это сработало, я использую следующие методы:

  • Преобразование всех возможных символов в их ascii/english equivivalent
  • Недопустимые символы, которые избегают этой очистки, буквально удаляются из строки

Но это не так, как мы теряем данные!

Диакритика для ASCII

/// <summary>
/// Converts all Diacritic characters in a string to their ASCII equivalent
/// Courtesy: http://stackoverflow.com/a/13154805/476786
/// A quick explanation:
/// * Normalizing to form D splits charactes like è to an e and a nonspacing `
/// * From this, the nospacing characters are removed
/// * The result is normalized back to form C (I'm not sure if this is neccesary)
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static string ConvertDiacriticToASCII(this string value)
{
    if (value == null) return null;
    var chars =
        value.Normalize(NormalizationForm.FormD)
             .ToCharArray()
             .Select(c => new {c, uc = CharUnicodeInfo.GetUnicodeCategory(c)})
             .Where(@t => @t.uc != UnicodeCategory.NonSpacingMark)
             .Select(@t => @t.c);
    var cleanStr = new string(chars.ToArray()).Normalize(NormalizationForm.FormC);
    return cleanStr;
}

Non-ASCII Burninator

/// <summary>
/// Removes all non-ASCII characters from the string
/// Courtesy: http://stackoverflow.com/a/135473/476786
/// Uses the .NET ASCII encoding to convert a string. 
/// UTF8 is used during the conversion because it can represent any of the original characters. 
/// It uses an EncoderReplacementFallback to to convert any non-ASCII character to an empty string.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static string RemoveNonASCII(this string value)
{
    string cleanStr = 
           Encoding.ASCII
                   .GetString(
                              Encoding.Convert(Encoding.UTF8,
                                               Encoding.GetEncoding(Encoding.ASCII.EncodingName,
                                                                    new EncoderReplacementFallback(string.Empty),
                                                                    new DecoderExceptionFallback()
                                                                    ),
                                               Encoding.UTF8.GetBytes(value)
                                               )
                              );
    return cleanStr;
}

Я действительно надеюсь получить ответ, поскольку обходной путь явно не идеален, и также не имеет смысла, почему это невозможно!

Ответ 3

Чтобы расширить ответ на bPratik, мы обнаружили, что метаданные кодировки Base64 работают хорошо. Мы используем этот метод расширения для кодирования и декодирования:

    public static class Base64Extensions
    {
        public static string ToBase64(this string input)
        {
            var bytes = Encoding.UTF8.GetBytes(input);
            return Convert.ToBase64String(bytes);
        }

        public static string FromBase64(this string input)
        {
            var bytes = Convert.FromBase64String(input);
            return Encoding.UTF8.GetString(bytes);
        }
    }

а затем при установке метаданных blob:

blobReference.Metadata["Filename"] = filename.ToBase64();

и при его получении:

var filename = blobReference.Metadata["Filename"].FromBase64();

Для поиска вам нужно будет декодировать имя файла перед его представлением в индекс или использовать фактическое имя файла blob, предполагая, что вы все еще используете исходное имя файла.

Ответ 4

Если приведенный выше список является исчерпывающим, он должен иметь возможность кодировать метаданные в HTML и затем декодировать его, когда вам это нужно:

var htmlEncodedValue = System.Web.HttpUtility.HtmlEncode(value)
var originalValue = System.Web.HttpUtility.HtmlDecode(value)