¿Cómo hago la codificación base64 en iOS?


230

Me gustaría base64codificar y decodificar, pero no pude encontrar ningún soporte del iPhone SDK. ¿Cómo puedo base64codificar y decodificar con o sin una biblioteca?


1
Hay una buena muestra de código al final de esta publicación. Muy autónomo ... BaseSixtyFour
Greg Bernhardt

El enlace @GregBernhardt está muerto.
Coeur

Respuestas:


116

Este es un buen caso de uso para las categorías del objetivo C .

Para codificación 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

Para decodificación 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

55
Si Obj-C es similar a C, debería poder hacer esto: static char base64EncodingTable [64] = "ABCDE [etc] 789 + /";
Artelius

3
Encontré por qué solo obtenía 4 caracteres ... Debe haber un} antes del retorno para el ciclo while (). Lo editaría pero no parece que pueda.
Larry Hipp

3
No es un error del analizador. Observe que el código también intenta acceder a inbuf [3] que está más allá de los límites de esa matriz. Este código apesta.
Mike Weller

1
¿Qué representa el valor de longitud?
MegaManX

3
A partir de iOS7, Apple ha expuesto su método de codificación base 64 nativo. Vea la respuesta de Rob a continuación para saber cómo usarlo mientras mantiene la compatibilidad con versiones anteriores.
Code Commander el

100

Una implementación realmente rápida que fue portada (y modificada / mejorada) de la biblioteca de PHP Core al código nativo de Objective-C está disponible en la clase QSStrings de la biblioteca QSUtilities . Hice un punto de referencia rápido: un archivo de imagen de 5,3 MB (JPEG) tardó <50 ms en codificarse y unos 140 ms en decodificarse.

El código para toda la biblioteca (incluidos los métodos Base64) está disponible en GitHub .

O, alternativamente, si desea que el código solo contenga los métodos Base64, lo he publicado aquí:

Primero, necesita las tablas de mapeo:

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

Para codificar:

+ (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;
}

Para decodificar:

+ (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
Finalmente una implementación correcta y eficiente. Gracias. Algunos de los otros códigos por aquí me dan miedo.
Mike Weller

44
La memoria asignada como strResulten el codificador parece estar perdida; solo necesita un free()al final (antes de regresar pero después NSString stringWithCString)
JosephH

2
En su encodeBase64WithData:método, ¿el primer parámetro de la llamada no calloc()necesita incrementarse en 1 para tener en cuenta el terminador nulo ( '\0') que agrega al final?
erikprice

1
El hecho de que Apple no ofrece esto hace que Dios quiere gatitos de asesinato ... muchos de ellos ...
dsingleton

2
He estado usando esto por un tiempo y parecía funcionar muy bien hasta que comencé a obtener algunos errores relacionados con la corrupción de la memoria y al usar guard malloc lo reduje a esta línea: * objPointer = '\ 0'; así que ten cuidado si usas esto en tus propias aplicaciones.
Mattia

72

Históricamente, lo habríamos dirigido a una de las muchas bibliotecas de base 64 de terceros (como se discutió en las otras respuestas) para convertir de datos binarios a cadenas de base 64 (y viceversa), pero iOS 7 ahora tiene codificación nativa de base 64 (y expone los métodos de iOS 4 previamente privados, en caso de que necesite admitir versiones anteriores de iOS).

Por lo tanto, para convertir NSDataa la NSStringrepresentación base 64, puede usar base64EncodedStringWithOptions. Si también debe admitir versiones de iOS anteriores a la 7.0, puede hacer lo siguiente:

NSString *string;

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

Y para convertir la base 64 de NSStringnuevo a NSDatausted puede usar initWithBase64EncodedString. Del mismo modo, si necesita admitir versiones de iOS anteriores a la 7.0, puede hacer lo siguiente:

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
}

Obviamente, si no necesita compatibilidad con versiones anteriores de iOS anteriores a 7.0, es aún más fácil, simplemente use base64EncodedStringWithOptionso initWithBase64EncodedString, respectivamente, y no se moleste con la verificación del tiempo de ejecución para versiones anteriores de iOS. De hecho, si usa el código anterior cuando su objetivo mínimo es iOS 7 o superior, en realidad obtendrá una advertencia del compilador sobre los métodos obsoletos. Entonces, en iOS 7 y versiones posteriores, simplemente convertiría a una cadena base 64 con:

NSString *string = [data base64EncodedStringWithOptions:kNilOptions];

y de regreso con:

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

Gracias por eso Rob. ¿Podría por favor explicar brevemente lo que escribió, " ... y expone los métodos de iOS 4 anteriormente privados "?
phi

8
Es una pena que esta respuesta esté enterrada debajo de todas esas implementaciones personalizadas. Es una debilidad de SO, donde una solución más apropiada puede haber surgido mucho después de que se hizo la pregunta original, esa solución ahora tiene que competir con lo que fue aceptado previamente.
jakev

Es por eso que siempre es útil votar más recientemente las respuestas correctas :)
Steve Wilford

por qué demonios las respuestas como esta no están en la parte superior :(, pasé mucho tiempo procesando todas las respuestas anteriores T__T
compilador de Alsh

33

iOS incluye soporte incorporado para codificación y decodificación base64. Si nos fijamos resolv.h, debería ver las dos funciones b64_ntopy b64_pton. El zócalo cuadrado biblioteca proporciona un ejemplo razonable de cómo usar estas funciones desde el objetivo-c.

Estas funciones están bastante probadas y son confiables, a diferencia de muchas de las implementaciones que puede encontrar en publicaciones aleatorias en Internet. No te olvides de enlazar en contra libresolv.dylib.


3
Increíble; mucho mejor que un sitio de internet al azar! En caso de que alguien esté preocupado por usar estas funciones escasamente documentadas, puede ver la fuente de estas en el sitio de Apple .
Jesse Rusak

1
Este tipo da más información al respecto: blog.montgomerie.net/ios-hidden-base64-routines
Mike

21

Dado que este parece ser el éxito número uno de Google en la codificación base64 y el iPhone, tuve ganas de compartir mi experiencia con el fragmento de código anterior.

Funciona, pero es extremadamente lento. Un punto de referencia en una imagen aleatoria (0.4 mb) tomó 37 segundos en el iPhone nativo. La razón principal es probablemente toda la magia OOP: NSStrings de un solo carácter, etc., que solo se liberan automáticamente después de que se realiza la codificación.

Otra sugerencia publicada aquí (ab) usa la biblioteca openssl, que también parece exagerada.

El siguiente código tarda 70 ms, es decir, una aceleración de 500 veces. Esto solo hace codificación base64 (la decodificación seguirá tan pronto como la encuentre)

+ (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;
}

Dejé el corte de línea ya que no lo necesitaba, pero es trivial agregarlo.

Para aquellos que estén interesados ​​en optimizar: el objetivo es minimizar lo que sucede en el ciclo principal. Por lo tanto, toda la lógica para tratar con los últimos 3 bytes se trata fuera del ciclo.

Además, intente trabajar con datos en el lugar, sin copia adicional a / desde buffers. Y reduzca cualquier aritmética al mínimo.

Observe que los bits que se juntan para buscar una entrada en la tabla no se superpondrían cuando debían juntarse sin cambiar. Por lo tanto, una mejora importante podría ser usar 4 tablas de búsqueda de 256 bytes separadas y eliminar los cambios, de esta manera:

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

Por supuesto, podría llevarlo mucho más lejos, pero eso está más allá del alcance aquí.


Hmm No pude hacer que esto funcione. Observo una codificación base64 diferente a mi valor esperado. ¿Has probado esto con los ejemplos en RFC 4648? tools.ietf.org/html/rfc4648
Alex Reynolds

3
Luchando para ver qué base64EncodingTable1, base64EncodingTable2, base64EncodingTable3 y base64EncodingTable4 hacen referencia?
Jamie Chapman

Muy útil, pero puede leer más allá del final del búfer de entrada. Cuando (izquierda == 2), raw [inp + 2] será un byte más allá del final de tmpbuf. Creo que la línea debería ser: if (left == 2) outbuf [outp ++] = base64EncodingTable [((raw [inp + 1] & 0x0F) << 2)];
John Lemberger el

cambie la siguiente línea <code> char tmpbuf [2] = {0,0}; </code> a <code> unsigned char tmpbuf [3] = {0,0,0}; </code>
Satya

9

En la excelente mejora de mvds, hay dos problemas. Cambie el código a esto:

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

Mejor solucion:

Hay una función incorporada en NSData

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

Podemos hacerlo en función de la versión de iOS en la que se ejecuta la aplicación utilizando "[[UIDevice currentDevice] systemVersion] .floatValue".
Nagaraj

2
1. Eso no le dirá con qué SDK se vinculó, eso es una verificación de tiempo de ejecución. 2. Eso es directamente contrario a la orientación de Apple. Debe verificar la disponibilidad de una función, no la versión del sistema.
quellish

6

Me alegra que a la gente le haya gustado. El final del juego fue un poco defectuoso, debo admitir. Además de establecer correctamente inp = 0, también deberías aumentar el tamaño de tmpbuf a 3, como

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

o dejar de lado el orring de raw [inp + 2]; si tuviéramos un [inp + 2] sin procesar! = 0 para este fragmento, todavía estaríamos en el ciclo, por supuesto ...

De cualquier manera funciona, puede considerar mantener el bloque de búsqueda de la tabla final idéntico al del bucle para mayor claridad. En la versión final que usé hice

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

Para agregar el ==

Lo siento, no revisé los RFC y esas cosas, ¡debería haber hecho un mejor trabajo!


3
ya tiene una cuenta aquí, ya que su respuesta anterior es en realidad una cuenta diferente. Además, esto debería ser una edición de eso o un comentario.
Alastair Pitts

@alastair, parece que obtiene una "cuenta" cada vez que publica una respuesta sin registrarse, después de limpiar las cookies. No pude conectarme a mi primera "cuenta" (incluso con el mismo correo electrónico y la misma dirección IP), así que lo puse allí como una nueva respuesta, lo siento. - Recién registrado!
mvds

3
¿Hay alguna posibilidad de que pueda editar esta respuesta en la anterior para que haya una versión correcta definitiva? ¡Gracias!
JosephH

6

Bajo iOS8 y uso posterior - (NSString *)base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)optionsde NSData


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

Aquí hay una versión compacta de Objective-C como categoría en NSData. Se necesita pensar un poco sobre ...

@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

Se puede agregar relleno si es necesario ampliando el alcance de 'byt' y agregando 'dest' con caracteres (2-byt) "=" antes de regresar.

Luego se puede agregar una Categoría a NSString, por lo tanto:

@implementation NSString (StringUtils)

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

 return [theData newStringInBase64FromData];
}

@end

2

iOS ha incorporado métodos de codificación y decodificación Base64 (sin usar libresolv) desde iOS 4. Sin embargo, solo se declaró en el SDK de iOS 7. La documentación de Apple indica que puede usarlo cuando apunta a iOS 4 y superior.

NSData *myData = ... some data
NSString *base64String = [myData base64Encoding];
NSData *decodedData = [[NSData alloc] initWithBase64Encoding:base64String];

2

Aquí hay un ejemplo para convertir un objeto NSData a Base 64. También muestra cómo ir al otro lado (decodificar un objeto NSData codificado en 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

en iOS 7

        NSData *data=[[NSData alloc]init];
        [data base64Encoding];

Nagaraj ya mencionó esto. Vea su publicación y los comentarios que lo cumplan que indican que ha estado allí desde iOS 4.
jww

1

Lo he hecho usando la siguiente clase ...

@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

Mientras llamaba llame

 [Base64Converter base64StringFromData:dataval length:lengthval];

Eso es...


1

Creo que esto será útil

 + (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? Por favor, dar una respuesta completa?
Mohsin Khubaib Ahmed

1
Estos son dos métodos que necesita escribir en cualquier clase y puede llamarlo y pasar instancias de cadena como parámetro.
Mrug

0

Descargar Base64

Haga el siguiente código para convertir una imagen a base64

NSString *base64String=[UIImagePNGRepresentation(image) base64Encoding];

0

Según sus requisitos, he creado una demostración de muestra usando Swift 4 en la que puede codificar / decodificar cadenas e imágenes según sus requisitos.

  • También he agregado métodos de muestra de operaciones relevantes.

    //
    //  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
        }
    
    
    }
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.