Как сделать кодирование base64 на iOS?

230

Я хотел бы сделать base64кодирование и декодирование, но я не мог найти поддержку от iPhone SDK. Как я могу сделать base64кодирование и декодирование с или без библиотеки?

BlueDolphin
источник
1
Внизу этого поста есть хороший пример кода. Очень автономный ... BaseSixtyFour
Грег Бернхардт
Ссылка @GregBernhardt не работает.
Cœur

Ответы:

116

Это хороший вариант использования для категорий Objective C .

Для кодировки Base64:

#import <Foundation/NSString.h>

@interface NSString (NSStringAdditions)

+ (NSString *) base64StringFromData:(NSData *)data length:(int)length;

@end

-------------------------------------------

#import "NSStringAdditions.h"

static char base64EncodingTable[64] = {
  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
  'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
  'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
  'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
};

@implementation NSString (NSStringAdditions)

+ (NSString *) base64StringFromData: (NSData *)data length: (int)length {
  unsigned long ixtext, lentext;
  long ctremaining;
  unsigned char input[3], output[4];
  short i, charsonline = 0, ctcopy;
  const unsigned char *raw;
  NSMutableString *result;

  lentext = [data length]; 
  if (lentext < 1)
    return @"";
  result = [NSMutableString stringWithCapacity: lentext];
  raw = [data bytes];
  ixtext = 0; 

  while (true) {
    ctremaining = lentext - ixtext;
    if (ctremaining <= 0) 
       break;        
    for (i = 0; i < 3; i++) { 
       unsigned long ix = ixtext + i;
       if (ix < lentext)
          input[i] = raw[ix];
       else
  input[i] = 0;
  }
  output[0] = (input[0] & 0xFC) >> 2;
  output[1] = ((input[0] & 0x03) << 4) | ((input[1] & 0xF0) >> 4);
  output[2] = ((input[1] & 0x0F) << 2) | ((input[2] & 0xC0) >> 6);
  output[3] = input[2] & 0x3F;
  ctcopy = 4;
  switch (ctremaining) {
    case 1: 
      ctcopy = 2; 
      break;
    case 2: 
      ctcopy = 3; 
      break;
  }

  for (i = 0; i < ctcopy; i++)
     [result appendString: [NSString stringWithFormat: @"%c", base64EncodingTable[output[i]]]];

  for (i = ctcopy; i < 4; i++)
     [result appendString: @"="];

  ixtext += 3;
  charsonline += 4;

  if ((length > 0) && (charsonline >= length))
    charsonline = 0;
  }     
  return result;
}

@end

Для декодирования Base64:

#import <Foundation/Foundation.h>

@class NSString;

@interface NSData (NSDataAdditions)

+ (NSData *) base64DataFromString:(NSString *)string;

@end

-------------------------------------------

#import "NSDataAdditions.h"

@implementation NSData (NSDataAdditions)

+ (NSData *)base64DataFromString: (NSString *)string
{
    unsigned long ixtext, lentext;
    unsigned char ch, inbuf[4], outbuf[3];
    short i, ixinbuf;
    Boolean flignore, flendtext = false;
    const unsigned char *tempcstring;
    NSMutableData *theData;

    if (string == nil)
    {
        return [NSData data];
    }

    ixtext = 0;

    tempcstring = (const unsigned char *)[string UTF8String];

    lentext = [string length];

    theData = [NSMutableData dataWithCapacity: lentext];

    ixinbuf = 0;

    while (true)
    {
        if (ixtext >= lentext)
        {
            break;
        }

        ch = tempcstring [ixtext++];

        flignore = false;

        if ((ch >= 'A') && (ch <= 'Z'))
        {
            ch = ch - 'A';
        }
        else if ((ch >= 'a') && (ch <= 'z'))
        {
            ch = ch - 'a' + 26;
        }
        else if ((ch >= '0') && (ch <= '9'))
        {
            ch = ch - '0' + 52;
        }
        else if (ch == '+')
        {
            ch = 62;
        }
        else if (ch == '=')
        {
            flendtext = true;
        }
        else if (ch == '/')
        {
            ch = 63;
        }
        else
        {
            flignore = true; 
        }

        if (!flignore)
        {
            short ctcharsinbuf = 3;
            Boolean flbreak = false;

            if (flendtext)
            {
                if (ixinbuf == 0)
                {
                    break;
                }

                if ((ixinbuf == 1) || (ixinbuf == 2))
                {
                    ctcharsinbuf = 1;
                }
                else
                {
                    ctcharsinbuf = 2;
                }

                ixinbuf = 3;

                flbreak = true;
            }

            inbuf [ixinbuf++] = ch;

            if (ixinbuf == 4)
            {
                ixinbuf = 0;

                outbuf[0] = (inbuf[0] << 2) | ((inbuf[1] & 0x30) >> 4);
                outbuf[1] = ((inbuf[1] & 0x0F) << 4) | ((inbuf[2] & 0x3C) >> 2);
                outbuf[2] = ((inbuf[2] & 0x03) << 6) | (inbuf[3] & 0x3F);

                for (i = 0; i < ctcharsinbuf; i++)
                {
                    [theData appendBytes: &outbuf[i] length: 1];
                }
            }

            if (flbreak)
            {
                break;
            }
        }
    }

    return theData;
}

    @end
Алекс Рейнольдс
источник
5
Если Obj-C похож на C, вы можете сделать это: static char base64EncodingTable [64] = "ABCDE [etc] 789 + /";
Артелиус
3
Я обнаружил, почему получаю только 4 символа ... Перед возвратом цикла while () должен быть символ}. Я бы отредактировал это, но я не выгляжу так, как могу.
Ларри Хипп
3
Это не ошибка анализатора. Обратите внимание, что код также пытается получить доступ к inbuf [3], который находится за пределами этого массива. Этот код воняет.
Майк Веллер
1
Что представляет собой длина?
MegaManX
3
Начиная с iOS7 Apple выставила свой родной метод кодирования base 64. См. Ответ Роба ниже о том, как его использовать при сохранении обратной совместимости.
Code Commander
100

Действительно, очень быстрая реализация, которая была перенесена (и изменена / улучшена) из библиотеки PHP Core в собственный код Objective-C, доступна в классе QSStrings из библиотеки QSUtilities . Я сделал быстрый тест: файл изображения размером 5,3 МБ (JPEG) занял менее 50 мс для кодирования и около 140 мс для декодирования.

Код для всей библиотеки (включая методы Base64) доступен на GitHub .

Или, в качестве альтернативы, если вы хотите, чтобы код просто самих методов Base64, я разместил его здесь:

Во-первых, вам нужны таблицы сопоставления:

static const char _base64EncodingTable[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const short _base64DecodingTable[256] = {
    -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -2, -1, -1, -2, -2,
    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
    -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 62, -2, -2, -2, 63,
    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -2, -2, -2, -2, -2, -2,
    -2,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -2, -2, -2, -2, -2,
    -2, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -2, -2, -2, -2, -2,
    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2
};

Кодировать:

+ (NSString *)encodeBase64WithString:(NSString *)strData {
    return [QSStrings encodeBase64WithData:[strData dataUsingEncoding:NSUTF8StringEncoding]];
}

+ (NSString *)encodeBase64WithData:(NSData *)objData {
    const unsigned char * objRawData = [objData bytes];
    char * objPointer;
    char * strResult;

    // Get the Raw Data length and ensure we actually have data
    int intLength = [objData length];
    if (intLength == 0) return nil;

    // Setup the String-based Result placeholder and pointer within that placeholder
    strResult = (char *)calloc((((intLength + 2) / 3) * 4) + 1, sizeof(char));
    objPointer = strResult;

    // Iterate through everything
    while (intLength > 2) { // keep going until we have less than 24 bits
        *objPointer++ = _base64EncodingTable[objRawData[0] >> 2];
        *objPointer++ = _base64EncodingTable[((objRawData[0] & 0x03) << 4) + (objRawData[1] >> 4)];
        *objPointer++ = _base64EncodingTable[((objRawData[1] & 0x0f) << 2) + (objRawData[2] >> 6)];
        *objPointer++ = _base64EncodingTable[objRawData[2] & 0x3f];

        // we just handled 3 octets (24 bits) of data
        objRawData += 3;
        intLength -= 3; 
    }

    // now deal with the tail end of things
    if (intLength != 0) {
        *objPointer++ = _base64EncodingTable[objRawData[0] >> 2];
        if (intLength > 1) {
            *objPointer++ = _base64EncodingTable[((objRawData[0] & 0x03) << 4) + (objRawData[1] >> 4)];
            *objPointer++ = _base64EncodingTable[(objRawData[1] & 0x0f) << 2];
            *objPointer++ = '=';
        } else {
            *objPointer++ = _base64EncodingTable[(objRawData[0] & 0x03) << 4];
            *objPointer++ = '=';
            *objPointer++ = '=';
        }
    }

    // Terminate the string-based result
    *objPointer = '\0';

    // Create result NSString object
    NSString *base64String = [NSString stringWithCString:strResult encoding:NSASCIIStringEncoding];

    // Free memory
    free(strResult);

    return base64String;
}

Расшифровать:

+ (NSData *)decodeBase64WithString:(NSString *)strBase64 {
    const char *objPointer = [strBase64 cStringUsingEncoding:NSASCIIStringEncoding];
    size_t intLength = strlen(objPointer);
    int intCurrent;
    int i = 0, j = 0, k;

    unsigned char *objResult = calloc(intLength, sizeof(unsigned char));

    // Run through the whole string, converting as we go
    while ( ((intCurrent = *objPointer++) != '\0') && (intLength-- > 0) ) {
        if (intCurrent == '=') {
            if (*objPointer != '=' && ((i % 4) == 1)) {// || (intLength > 0)) {
                // the padding character is invalid at this point -- so this entire string is invalid
                free(objResult);
                return nil;
            }
            continue;
        }

        intCurrent = _base64DecodingTable[intCurrent];
        if (intCurrent == -1) {
            // we're at a whitespace -- simply skip over
            continue;
        } else if (intCurrent == -2) {
            // we're at an invalid character
            free(objResult);
            return nil;
        }

        switch (i % 4) {
            case 0:
                objResult[j] = intCurrent << 2;
                break;

            case 1:
                objResult[j++] |= intCurrent >> 4;
                objResult[j] = (intCurrent & 0x0f) << 4;
                break;

            case 2:
                objResult[j++] |= intCurrent >>2;
                objResult[j] = (intCurrent & 0x03) << 6;
                break;

            case 3:
                objResult[j++] |= intCurrent;
                break;
        }
        i++;
    }

    // mop things up if we ended on a boundary
    k = j;
    if (intCurrent == '=') {
        switch (i % 4) {
            case 1:
                // Invalid state
                free(objResult);
                return nil;

            case 2:
                k++;
                // flow through
            case 3:
                objResult[k] = 0;
        }
    }

    // Cleanup and setup the return NSData
    NSData * objData = [[[NSData alloc] initWithBytes:objResult length:j] autorelease];
    free(objResult);
    return objData;
}
Майк Хо
источник
2
Наконец правильная и эффективная реализация. Спасибо. Какой-то другой код здесь пугает меня.
Майк Веллер
4
Память, выделенная как strResultв кодере, похоже, просочилась; это просто нужно free()в конце (до возвращения, но после NSString stringWithCString)
JosephH
2
В вашем encodeBase64WithData:методе не calloc()нужно ли увеличивать первый параметр в вызове на 1, чтобы учесть нулевой терминатор ( '\0'), который вы добавляете в конце?
erikprice
1
Тот факт, что яблоко не дает этого, заставляет Бога хотеть убивать котят ... многих из них ...
Динглтон
2
Я использовал это некоторое время, и это, казалось, работало замечательно, пока я не начал получать некоторые ошибки, связанные с повреждением памяти, и используя guard malloc, я сузил его до этой строки: * objPointer = '\ 0'; так что будьте осторожны, если вы используете это в своих собственных приложениях.
Маттиа
72

Исторически мы бы направили вас к одной из множества сторонних библиотек base 64 (как обсуждалось в других ответах) для преобразования из двоичных данных в строку base 64 (и обратно), но iOS 7 теперь имеет встроенную кодировку base 64 (и предоставляет доступ к ранее закрытым методам iOS 4 на случай, если вам потребуется поддержка более ранних версий iOS).

Таким образом, чтобы преобразовать NSDataв NSStringбазовое представление 64 вы можете использовать base64EncodedStringWithOptions. Если вам необходимо поддерживать версии iOS до 7.0, вы можете сделать:

NSString *string;

if ([data respondsToSelector:@selector(base64EncodedStringWithOptions:)]) {
    string = [data base64EncodedStringWithOptions:kNilOptions];  // iOS 7+
} else {
    string = [data base64Encoding];                              // pre iOS7
}

И конвертировать базу 64 NSStringобратно NSDataможно использовать initWithBase64EncodedString. Аналогично, если вам нужно поддерживать версии iOS до 7.0, вы можете сделать:

NSData *data;

if ([NSData instancesRespondToSelector:@selector(initWithBase64EncodedString:options:)]) {
    data = [[NSData alloc] initWithBase64EncodedString:string options:kNilOptions];  // iOS 7+
} else {
    data = [[NSData alloc] initWithBase64Encoding:string];                           // pre iOS7
}

Очевидно, что если вам не нужна обратная совместимость с версиями iOS до 7.0, это даже проще, просто используйте base64EncodedStringWithOptionsили initWithBase64EncodedString, соответственно, и не беспокойтесь о проверке во время выполнения для более ранних версий iOS. Фактически, если вы используете приведенный выше код, когда ваша минимальная цель - iOS 7 или выше, вы фактически получите предупреждение компилятора об устаревших методах. Итак, в iOS 7 и выше вы просто конвертируете в строку base 64 строку с:

NSString *string = [data base64EncodedStringWithOptions:kNilOptions];

и снова с:

NSData *data = [[NSData alloc] initWithBase64EncodedString:string options:kNilOptions]; 
обкрадывать
источник
Спасибо за этого Роба. Не могли бы вы вкратце рассказать о том, что вы написали: « ... и раскрыть ранее частные методы iOS 4 »?
Phi
8
Жаль, что этот ответ похоронен под всеми этими пользовательскими реализациями. Это слабость SO, когда более подходящее решение могло появиться спустя много времени после того, как был задан первоначальный вопрос, и теперь это решение должно конкурировать с тем, что было принято ранее.
Яков
Вот почему всегда полезно высказывать более правильные ответы в последнее время :)
Стив Уилфорд
почему, черт возьми, такие ответы не на высоте :(, я потратил много времени на обработку всех ответов выше T__T
Alsh compiler
33

iOS включает встроенную поддержку для кодирования и декодирования base64. Если вы посмотрите на resolv.hвы должны увидеть две функции b64_ntopи b64_pton. Библиотека Square SocketRocket предоставляет разумный пример того, как использовать эти функции из target-c.

Эти функции довольно хорошо протестированы и надежны - в отличие от многих реализаций, которые вы можете найти в случайных публикациях в Интернете. Не забудьте связать против libresolv.dylib.

quellish
источник
3
Потрясающие; намного лучше, чем случайный интернет-сайт! В случае, если кто-то беспокоится об использовании этих скудно документированных функций, вы можете увидеть их источник на сайте Apple .
Джесси Русак
1
Этот парень рассказывает
Майк
21

Так как это, похоже, хит номер один в Google по кодировке base64 и iphone, я решил поделиться своим опытом с фрагментом кода выше.

Это работает, но это очень медленно. Тест на случайном изображении (0,4 МБ) на родном iphone занял 37 секунд. Основной причиной, вероятно, является вся магия ООП - одиночные символьные строки NSS и т. Д., Которые автоматически высвобождаются только после завершения кодирования.

Другое предложение, размещенное здесь (ab), использует библиотеку openssl, которая также выглядит как излишняя.

Код ниже занимает 70 мс - это ускорение в 500 раз. Это только делает кодирование base64 (декодирование последует, как только я столкнусь с ним)

+ (NSString *) base64StringFromData: (NSData *)data length: (int)length {
int lentext = [data length]; 
if (lentext < 1) return @"";

char *outbuf = malloc(lentext*4/3+4); // add 4 to be sure

if ( !outbuf ) return nil;

const unsigned char *raw = [data bytes];

int inp = 0;
int outp = 0;
int do_now = lentext - (lentext%3);

for ( outp = 0, inp = 0; inp < do_now; inp += 3 )
{
    outbuf[outp++] = base64EncodingTable[(raw[inp] & 0xFC) >> 2];
    outbuf[outp++] = base64EncodingTable[((raw[inp] & 0x03) << 4) | ((raw[inp+1] & 0xF0) >> 4)];
    outbuf[outp++] = base64EncodingTable[((raw[inp+1] & 0x0F) << 2) | ((raw[inp+2] & 0xC0) >> 6)];
    outbuf[outp++] = base64EncodingTable[raw[inp+2] & 0x3F];
}

if ( do_now < lentext )
{
    char tmpbuf[2] = {0,0};
    int left = lentext%3;
    for ( int i=0; i < left; i++ )
    {
        tmpbuf[i] = raw[do_now+i];
    }
    raw = tmpbuf;
    outbuf[outp++] = base64EncodingTable[(raw[inp] & 0xFC) >> 2];
    outbuf[outp++] = base64EncodingTable[((raw[inp] & 0x03) << 4) | ((raw[inp+1] & 0xF0) >> 4)];
    if ( left == 2 ) outbuf[outp++] = base64EncodingTable[((raw[inp+1] & 0x0F) << 2) | ((raw[inp+2] & 0xC0) >> 6)];
}

NSString *ret = [[[NSString alloc] initWithBytes:outbuf length:outp encoding:NSASCIIStringEncoding] autorelease];
free(outbuf);

return ret;
}

Я пропустил обрезку строк, так как мне это не нужно, но добавить тривиально.

Для тех, кто заинтересован в оптимизации: цель состоит в том, чтобы минимизировать то, что происходит в основном цикле. Поэтому вся логика обработки последних 3 байтов обрабатывается вне цикла.

Также попробуйте работать с данными на месте, без дополнительного копирования в / из буферов. И уменьшите любую арифметику до минимума.

Заметьте, что биты, которые собираются вместе для поиска записи в таблице, не будут перекрываться, когда они должны быть упорядочены вместе без смещения. Поэтому основным улучшением может стать использование 4 отдельных 256-байтовых таблиц поиска и устранение сдвигов, например:

outbuf[outp++] = base64EncodingTable1[(raw[inp] & 0xFC)];
outbuf[outp++] = base64EncodingTable2[(raw[inp] & 0x03) | (raw[inp+1] & 0xF0)];
outbuf[outp++] = base64EncodingTable3[(raw[inp+1] & 0x0F) | (raw[inp+2] & 0xC0)];
outbuf[outp++] = base64EncodingTable4[raw[inp+2] & 0x3F];

Конечно, вы можете пойти еще дальше, но это выходит за рамки этого.

MVDS
источник
Хм. Я не мог заставить это работать. Я наблюдаю другую кодировку base64, чем мое ожидаемое значение. Вы проверяли это на примерах из RFC 4648? tools.ietf.org/html/rfc4648
Алекс Рейнольдс
3
Пытаетесь понять, на что ссылаются base64EncodingTable1, base64EncodingTable2, base64EncodingTable3 и base64EncodingTable4?
Джейми Чепмен
Очень полезно, но может читать за пределами входного буфера. Когда (слева == 2), raw [inp + 2] будет на один байт после конца tmpbuf. Я думаю, что строка должна быть: if (left == 2) outbuf [outp ++] = base64EncodingTable [((raw [inp + 1] & 0x0F) << 2)];
Джон Лембергер
измените следующую строку <code> char tmpbuf [2] = {0,0}; </ code> на <code> unsigned char tmpbuf [3] = {0,0,0}; </ code>
Сатья
9

В отличном улучшении mvds есть две проблемы. Изменить код на это:

raw = tmpbuf;
inp = 0;
outbuf[outp++] = base64EncodingTable[(raw[inp] & 0xFC) >> 2];
outbuf[outp++] = base64EncodingTable[((raw[inp] & 0x03) << 4) | ((raw[inp+1] & 0xF0) >> 4)];
if ( left == 2 ) outbuf[outp++] = base64EncodingTable[((raw[inp+1] & 0x0F) << 2) | ((raw[inp+2] & 0xC0) >> 6)];
else outbuf[outp++] = '=';
outbuf[outp++] = '=';
user335742
источник
9

Лучшее решение:

В NSData есть встроенная функция

[data base64Encoding]; //iOS < 7.0
[data base64EncodedStringWithOptions:NSDataBase64Encoding76CharacterLineLength]; //iOS >= 7.0
Нагарадж
источник
Мы можем сделать это на основе версии iOS, в которой запущено приложение, используя «[[UIDevice currentDevice] systemVersion] .floatValue».
Нагарадж,
2
1. Это не скажет вам, с каким SDK вы связались, это проверка во время выполнения. 2. Это прямо противоречит руководству Apple. Вы должны проверять наличие функции, а не версию системы.
безжалостно
6

Рад, что людям понравилось. Конечная игра была немного испорчена, я должен признать. Помимо правильной установки inp = 0, вы должны либо увеличить размер tmpbuf до 3, например

unsigned char tmpbuf[3] = {0,0,0};

или пропустите ряд необработанных [inp + 2]; если бы у нас был сырой [inp + 2]! = 0 для этого чанка, мы все равно были бы в цикле ...

В любом случае, для наглядности вы можете оставить блок поиска окончательной таблицы идентичным блоку в цикле. В финальной версии я использовал я сделал

while ( outp%4 ) outbuf[outp++] = '=';

Добавить ==

Извините, я не проверял RFC и прочее, должен был сделать лучшую работу!

MVDS
источник
3
у вас уже есть учетная запись здесь, так как ваш предыдущий ответ на самом деле другой учетной записи. Кроме того, это должно быть либо редактирование этого, либо комментарий.
Аластер Питтс
@alastair, вы, кажется, получаете «учетную запись» каждый раз, когда публикуете ответ без регистрации, после очистки куки. Я не смог подключиться к своей первой «учетной записи» (даже с тем же адресом электронной почты и IP-адресом), поэтому я просто добавил его в качестве нового ответа, извините за это. - только что зарегистрировался!
mvds
3
Есть ли шанс, что вы сможете отредактировать этот ответ в своем предыдущем, чтобы была точная правильная версия? Спасибо!
JosephH
6

Под iOS8 и позже используйте - (NSString *)base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)optionsNSData

AlexeyVMP
источник
3
#import "NSDataAdditions.h"
@implementation NSData (NSDataAdditions)

+ (NSData *) base64DataFromString: (NSString *)string {
  unsigned long ixtext, lentext;
  unsigned char ch, input[4], output[3];
  short i, ixinput;
  Boolean flignore, flendtext = false;
  const char *temporary;
  NSMutableData *result;

  if (!string)
    return [NSData data];

  ixtext = 0;
  temporary = [string UTF8String];
  lentext = [string length];
  result = [NSMutableData dataWithCapacity: lentext];
  ixinput = 0;

  while (true) {
    if (ixtext >= lentext)
      break;
    ch = temporary[ixtext++];
    flignore = false;

    if ((ch >= 'A') && (ch <= 'Z'))
      ch = ch - 'A';
    else if ((ch >= 'a') && (ch <= 'z'))
      ch = ch - 'a' + 26;
    else if ((ch >= '0') && (ch <= '9'))
      ch = ch - '0' + 52;
    else if (ch == '+')
      ch = 62;
    else if (ch == '=')
      flendtext = true;
    else if (ch == '/')
      ch = 63;
    else
      flignore = true;

    if (!flignore) {
      short ctcharsinput = 3;
      Boolean flbreak = false;

      if (flendtext) {
         if (ixinput == 0)
           break;              
         if ((ixinput == 1) || (ixinput == 2))
           ctcharsinput = 1;
         else
           ctcharsinput = 2;
         ixinput = 3;
         flbreak = true;
      }

      input[ixinput++] = ch;

      if (ixinput == 4){
        ixinput = 0;
        output[0] = (input[0] << 2) | ((input[1] & 0x30) >> 4);
        output[1] = ((input[1] & 0x0F) << 4) | ((input[2] & 0x3C) >> 2);
        output[2] = ((input[2] & 0x03) << 6) | (input[3] & 0x3F);
        for (i = 0; i < ctcharsinput; i++)
        [result appendBytes: &output[i] length: 1];
      }
    if (flbreak)
      break;
    }
  }
  return result;
}
@end
alpha09jp
источник
2

Вот компактная версия Objective C как Категория на NSData. Нужно подумать о ...

@implementation NSData (DataUtils)

static char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

- (NSString *)newStringInBase64FromData
{
 NSMutableString *dest = [[NSMutableString alloc] initWithString:@""];
 unsigned char * working = (unsigned char *)[self bytes];
 int srcLen = [self length];

 // tackle the source in 3's as conveniently 4 Base64 nibbles fit into 3 bytes
 for (int i=0; i<srcLen; i += 3)
 {
  // for each output nibble
  for (int nib=0; nib<4; nib++)
  {
   // nibble:nib from char:byt
   int byt = (nib == 0)?0:nib-1;
   int ix = (nib+1)*2;

   if (i+byt >= srcLen) break;

   // extract the top bits of the nibble, if valid
   unsigned char curr = ((working[i+byt] << (8-ix)) & 0x3F);

   // extract the bottom bits of the nibble, if valid
   if (i+nib < srcLen) curr |= ((working[i+nib] >> ix) & 0x3F);

   [dest appendFormat:@"%c", base64[curr]];
  }
 }

 return dest;
}

@end

При необходимости можно добавить заполнение, сделав область 'byt' шире и добавив 'dest' с (2-byt) символами "=" перед возвратом.

Затем можно добавить категорию в NSString, таким образом:

@implementation NSString (StringUtils)

- (NSString *)newStringInBase64FromString
{
 NSData *theData = [NSData dataWithBytes:[self UTF8String] length:[self length]]; 

 return [theData newStringInBase64FromData];
}

@end

источник
2

iOS имеет встроенные методы кодирования и декодирования Base64 (без использования libresolv) начиная с iOS 4. Однако это было объявлено только в iOS 7 SDK. В документации Apple говорится, что вы можете использовать ее для iOS 4 и выше.

NSData *myData = ... some data
NSString *base64String = [myData base64Encoding];
NSData *decodedData = [[NSData alloc] initWithBase64Encoding:base64String];
user102008
источник
2

Вот пример преобразования объекта NSData в Base 64. Здесь также показано, как пойти другим путем (декодировать объект NSData, закодированный в Base 64):

NSData *dataTake2 = 
  [@"iOS Developer Tips" dataUsingEncoding:NSUTF8StringEncoding];

// Convert to Base64 data
NSData *base64Data = [dataTake2 base64EncodedDataWithOptions:0];

// Do something with the data...

// Now convert back from Base64
NSData *nsdataDecoded = [base64Data initWithBase64EncodedData:base64Data options:0];
Джон Мухов
источник
1

в iOS 7

        NSData *data=[[NSData alloc]init];
        [data base64Encoding];
Mani
источник
Нагарадж уже упоминал об этом. См. Его пост и комментарии к нему, в которых говорится, что он был там с iOS 4.
2011 г.
1

Я сделал это, используя следующий класс ..

@implementation Base64Converter
static char base64EncodingTable[64] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',  'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',    '8', '9', '+', '/'
};
+ (NSString *) base64StringFromData: (NSData *)data length: (int)length {

unsigned long ixtext, lentext;

long ctremaining;

unsigned char input[3], output[4];

short i, charsonline = 0, ctcopy;

const unsigned char *raw;

NSMutableString *result;

lentext = [data length];

if (lentext < 1)
    return @"";

result = [NSMutableString stringWithCapacity: lentext];

raw = [data bytes];

ixtext = 0;

while (true) {

    ctremaining = lentext - ixtext;

    if (ctremaining <= 0)
        break;

    for (i = 0; i < 3; i++) {
        unsigned long ix = ixtext + i;
        if (ix < lentext)
            input[i] = raw[ix];
        else
            input[i] = 0;
    }

    output[0] = (input[0] & 0xFC) >> 2;

    output[1] = ((input[0] & 0x03) << 4) | ((input[1] & 0xF0) >> 4);

    output[2] = ((input[1] & 0x0F) << 2) | ((input[2] & 0xC0) >> 6);

    output[3] = input[2] & 0x3F;

    ctcopy = 4;

    switch (ctremaining) {
        case 1:
            ctcopy = 2;
            break;

        case 2:
            ctcopy = 3;
            break;
    }

    for (i = 0; i < ctcopy; i++)
        [result appendString: [NSString stringWithFormat: @"%c", base64EncodingTable[output[i]]]];

    for (i = ctcopy; i < 4; i++)
        [result appendString: @"="];

    ixtext += 3;

    charsonline += 4;

    if ((length > 0) && (charsonline >= length))
        charsonline = 0;
}
return result;
}
@end

Во время звонка

 [Base64Converter base64StringFromData:dataval length:lengthval];

Это оно...

Durai Amuthan.H
источник
1

Я думаю, что это будет полезно

 + (NSString *)toBase64String:(NSString *)string {
    NSData *data = [string dataUsingEncoding: NSUnicodeStringEncoding];

    NSString *ret = [data base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];

    return ret;
    }

    + (NSString *)fromBase64String:(NSString *)string {
NSData *aData = [string dataUsingEncoding:NSUTF8StringEncoding];
NSData *aDataDecoded = [[NSData alloc]initWithBase64EncodedString:string options:0];
NSString *decryptedStr = [[NSString alloc]initWithData:aDataDecoded encoding:NSUTF8StringEncoding];

return [decryptedStr autorelease];

}

Mrug
источник
NSStringUtil? Пожалуйста, дайте полный ответ?
Мохсин Хубайб Ахмед
1
Это два метода, которые нужно написать в любом классе, и вы можете вызвать его и передать в качестве параметра параметры String.
Mrug
0

Загрузить Base64

Выполните следующий код для преобразования изображения в base64

NSString *base64String=[UIImagePNGRepresentation(image) base64Encoding];
Панкадж Вадхва
источник
0

Согласно вашему требованию я создал демонстрационную демонстрацию с использованием Swift 4, в которой вы можете кодировать / декодировать строку и изображение согласно вашему требованию.

  • Я также добавил примеры методов соответствующих операций.

    //
    //  Base64VC.swift
    //  SOF_SortArrayOfCustomObject
    //
    //  Created by Test User on 09/01/18.
    //  Copyright © 2018 Test User. All rights reserved.
    //
    
    import UIKit
    import Foundation
    
    class Base64VC: NSObject {
    
        //----------------------------------------------------------------
        // MARK:-
        // MARK:- String to Base64 Encode Methods
        //----------------------------------------------------------------
    
        func sampleStringEncodingAndDecoding() {
            if let base64String = self.base64Encode(string: "TestString") {
                print("Base64 Encoded String: \n\(base64String)")
                if let originalString = self.base64Decode(base64String: base64String) {
                    print("Base64 Decoded String: \n\(originalString)")
                }
            }
        }
    
    
        //----------------------------------------------------------------
    
        func base64Encode(string: String) -> String? {
            if let stringData = string.data(using: .utf8) {
                return stringData.base64EncodedString()
            }
            return nil
        }
    
        //----------------------------------------------------------------
    
        func base64Decode(base64String: String) -> String? {
            if let base64Data = Data(base64Encoded: base64String) {
                return String(data: base64Data, encoding: .utf8)
            }
            return nil
        }
    
    
        //----------------------------------------------------------------
        // MARK:-
        // MARK:- Image to Base64 Encode  Methods
        //----------------------------------------------------------------
    
        func sampleImageEncodingAndDecoding() {
            if let base64ImageString = self.base64Encode(image: UIImage.init(named: "yourImageName")!) {
                print("Base64 Encoded Image: \n\(base64ImageString)")
                if let originaImage = self.base64Decode(base64ImageString: base64ImageString) {
                    print("originalImageData \n\(originaImage)")
                }
            }
        }
    
        //----------------------------------------------------------------
    
        func base64Encode(image: UIImage) -> String? {
            if let imageData = UIImagePNGRepresentation(image) {
                return imageData.base64EncodedString()
            }
            return nil
        }
    
        //----------------------------------------------------------------
    
        func base64Decode(base64ImageString: String) -> UIImage? {
            if let base64Data = Data(base64Encoded: base64ImageString) {
                return UIImage(data: base64Data)!
            }
            return nil
        }
    
    
    }
команда iOS
источник