Як зробити кодування base64 на iOS?


230

Я хотів би зробити base64кодування та розшифровку, але я не зміг знайти жодної підтримки від iPhone SDK. Як я можу зробити base64кодування та декодування з бібліотекою або без неї?


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 [тощо] 789 + /";
Артелій

3
Я виявив, чому я отримую лише 4 символи ... Потрібно мати}} перед поверненням циклу while (). Я б редагував це, але я не схожий на те, що я можу.
Ларрі Хіпп

3
Це не помилка аналізатора. Зверніть увагу, що код також намагається отримати доступ до inbuf [3], який знаходиться за межами цього масиву. Цей код смердить.
Майк Веллер

1
Що являє собою величина довжини?
MegaManX

3
Станом на iOS7, Apple представила свій метод кодування на базі 64. Дивіться відповідь Роба нижче про те, як його використовувати, зберігаючи зворотну сумісність.
Командир коду

100

Дійсно, дуже швидка реалізація , яка була перенесена (і модифіковано / покращено) з бібліотеки PHP ядра в машинний код 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
Той факт, що яблуко цього не забезпечує, Бог хоче вбити кошенят ... їх багато ...
dsingleton

2
Я використовую це деякий час, і, здавалося, це спрацювало чудово, поки я не почав отримувати деякі помилки, пов’язані з пошкодженням пам’яті, і за допомогою захисного програмного забезпечення malloc я звузив його до цього рядка: * objPointer = '\ 0'; тому будьте обережні, якщо ви використовуєте це у власних додатках.
Маттіа

72

Історично ми би направили вас до однієї з багатьох сторонніх базових 64 бібліотек (як обговорювалося в інших відповідях) для перетворення бінарних даних у базу 64 рядків (і назад), але iOS 7 тепер має кодовану базу 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 і новіших версіях ви просто перетворите на базовий рядок 64 із:

NSString *string = [data base64EncodedStringWithOptions:kNilOptions];

і знову з:

NSData *data = [[NSData alloc] initWithBase64EncodedString:string options:kNilOptions]; 

Дякую про це Роб. Не могли б ви детально розібратися з написаним вами " ... і викривати раніше приватні методи iOS 4 "?
фік

8
Прикро, що ця відповідь похована під усіма тими спеціалізованими реалізаціями. Це слабкість SO, коли більш прийнятне рішення може виникнути черездовго після того, як було запропоновано оригінальне питання, тепер це рішення повинно конкурувати з тим, що було прийнято раніше.
jakev

Ось чому завжди корисно оскаржувати останнім часом правильні відповіді :)
Стів Вілфорд,

чому, пекло, відповіді, як це, не зверху :(, я витратив багато часу на обробку всіх відповідей вище T__T
компілятор Alsh

33

iOS включає вбудовану підтримку кодування та декодування base64. Якщо ви дивитесь, resolv.hви повинні побачити дві функції b64_ntopі b64_pton. Бібліотека Square SocketRocket надає розумний приклад того, як використовувати ці функції від aim -c.

Ці функції досить добре перевірені та надійні - на відміну від багатьох реалізацій, які ви можете знайти у довільних Інтернет-повідомленнях. Не забувайте посилатися проти libresolv.dylib.


3
Чудовий; набагато краще, ніж випадковий Інтернет-сайт! Якщо хтось турбується про використання цих мізерно задокументованих функцій, ви можете побачити їх джерело на сайті Apple .
Джессі Русак

1
Цей хлопець наводить ще кілька відомостей про це: blog.montgomerie.net/ios-hidden-base64-routines
Майк

21

Оскільки це, мабуть, потрапило в Google кодування номер один на кодування base64 та iphone, я відчув, як поділитися своїм досвідом із фрагментом коду вище.

Це працює, але це надзвичайно повільно. Тест на випадковому зображенні (0,4 мб) зайняв 37 секунд на рідному iphone. Основна причина - це, мабуть, вся магія OOP - одиночні символи NSStrings тощо, які випускаються автоматично лише після кодування.

Інша пропозиція, розміщена тут (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];

Звичайно, ви могли б зробити це набагато далі, але це не виходить тут.


Хм. Я не міг змусити це працювати. Я спостерігаю, що кодування base64 відрізняється від очікуваного. Ви перевірили це на прикладах в RFC 4648? tools.ietf.org/html/rfc4648
Алекс Рейнольдс

3
Намагаєтесь побачити, на що посилаються base64EncodingTable1, base64EncodingTable2, base64EncodingTable3 та base64EncodingTable4?
Джеймі Чапман

Дуже корисно, але він може читатись поза кінцем буфера введення. Коли (зліва == 2), raw [inp + 2] буде на один байт поза межами кінця tmpbuf. Я думаю, що рядок повинен бути: if (зліва == 2) outbuf [outp ++] = base64EncodingTable [((raw [inp + 1] & 0x0F) << 2)];
Джон Лембергер

змінити наступний рядок <code> char tmpbuf [2] = {0,0}; </code> на <code> неподписаний char tmpbuf [3] = {0,0,0}; </code>
Satya

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++] = '=';

9

Краще рішення:

У NSData є вбудована функція

[data base64Encoding]; //iOS < 7.0
[data base64EncodedStringWithOptions:NSDataBase64Encoding76CharacterLineLength]; //iOS >= 7.0

Це можна зробити на основі версії iOS, на якій працює програма, використовуючи "[[UIDevice currentDevice] systemVersion] .floatValue".
Нагарай

2
1. Це не скаже вам, з яким SDK ви пов’язали, це перевірка виконання. 2. Це прямо суперечить вказівкам Apple. Ви повинні перевірити наявність функції, а не версії системи.
quellish

6

Рада людям сподобалось. Маю визнати, що кінцева гра була трохи хибною. Крім правильного встановлення inp = 0, ви також повинні збільшити розмір tmpbuf до 3, як

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

або залиште оруднення сирої [inp + 2]; якби у нас був сирий [inp + 2]! = 0 для цього шматка, ми все одно були б у циклі, звичайно ...

У будь-якому випадку працює, ви можете розглянути можливість збереження блоку пошуку остаточної таблиці ідентичним тому, що знаходиться в циклі. У остаточній версії, яку я використав, я зробив

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

Щоб додати ==

Вибачте, що не перевірив RFC та інше, мав би зробити кращу роботу!


3
у вас вже є обліковий запис, оскільки ваша попередня відповідь - це фактично інший рахунок. Також це має бути або редакцією до цього, або коментарем.
Аластер Піттс

@alastair, ви, здається, отримуєте "акаунт" щоразу, коли ви публікуєте відповідь без реєстрації, після очищення файлів cookie. Мені не вдалося підключитися до мого першого "акаунта" (навіть з однаковою електронною поштою та ip-адресою), тому я просто поставив його як нову відповідь, вибачте за це. - щойно зареєстровано!
mvds

3
Будь-який шанс ви зможете відредагувати цю відповідь у попередній, щоб була остаточна правильна версія? Дякую!
ДжозефH

6

Під iOS8 та пізнішим використанням - (NSString *)base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)optionsNSData


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


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-байт) "=" перед поверненням.

Потім Категорія може бути додана до 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];

2

Ось приклад для перетворення об’єкта NSData в Base 64. Він також показує, як піти іншим шляхом (декодувати базовий 64, закодований об'єкт NSData):

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];

Нагарадж уже згадував про це. Перегляньте його повідомлення та коментарі, які досягли його, коли він був там з iOS 4.
jww

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];

Це воно...


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];

}


NSStringUtil? Будь ласка, дайте повну відповідь?
Мохсін Хубайб Ахмед

1
Це два способи, які потрібно записати в будь-який Клас, і ви можете викликати його та передавати String instaces як параметр.
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
        }
    
    
    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.