Como faço para codificar base64 no iOS?

230

Gostaria de base64codificar e decodificar, mas não consegui encontrar nenhum suporte do iPhone SDK. Como posso base64codificar e decodificar com ou sem uma biblioteca?

BlueDolphin
fonte
1
Há um bom exemplo de código na parte inferior desta postagem. Muito independente ... BaseSixtyFour
Greg Bernhardt
O link @GregBernhardt está morto.
Cœur

Respostas:

116

Este é um caso bom uso para Objective C categorias .

Para codificação 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 decodificação 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
Alex Reynolds
fonte
5
Se Obj-C for semelhante a C, você poderá fazer isso: static char base64EncodingTable [64] = "ABCDE [etc] 789 + /";
Artelius 5/05/2009
3
Eu descobri por que estava recebendo apenas 4 caracteres ... Precisa haver um} antes do retorno do loop while (). Gostaria de editá-lo, mas não parece que posso.
Larry Hipp
3
Não é um bug do analisador. Observe que o código também tenta acessar o inbuf [3], que está além dos limites dessa matriz. Este código fede.
8118 Mike Weller
1
O que o valor do comprimento representa?
MegaManX
3
A partir do iOS7, a Apple expôs seu método de codificação de base 64 nativo. Consulte a resposta de Rob abaixo para saber como usá-lo, mantendo a compatibilidade com versões anteriores.
Code Commander
100

Uma implementação muito, muito rápida, que foi transportada (e modificada / aprimorada) da biblioteca PHP Core para o código Objective-C nativo está disponível na classe QSStrings da QSUtilities Library . Fiz uma referência rápida: um arquivo de imagem (JPEG) de 5,3 MB levou <50ms para codificar e cerca de 140ms para decodificar.

O código para toda a biblioteca (incluindo os Métodos Base64) está disponível no GitHub .

Ou, como alternativa, se você deseja que o código seja apenas dos métodos Base64, eu o publiquei aqui:

Primeiro, você precisa das tabelas de mapeamento:

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;
}
Mike Ho
fonte
2
Finalmente, uma implementação correta e eficiente. Obrigado. Alguns dos outros códigos por aqui me assustam.
Mike Weller
4
A memória alocada como strResultno codificador parece ter vazado; ele só precisa de um free()no final (antes de retornar, mas depois NSString stringWithCString)
Josephh
2
No seu encodeBase64WithData:método, o primeiro parâmetro na chamada não calloc()precisa ser incrementado em 1 para contabilizar o terminador nulo ( '\0') adicionado no final?
erikprice 30/01
1
O fato de que a Apple não fornece isto faz Deus quer gatinhos assassinato ... um monte deles ...
dsingleton
2
Eu tenho usado isso por um tempo e pareceu funcionar muito bem até que comecei a obter alguns erros relacionados à corrupção de memória e usando o guarda malloc, reduzi-o a esta linha: * objPointer = '\ 0'; tenha cuidado se você usar isso em seus próprios aplicativos.
30512 Mattia
72

Historicamente, teríamos direcionado você a uma das muitas bibliotecas da base 64 de terceiros (como discutido nas outras respostas) para converter de dados binários em string da base 64 (e vice-versa), mas o iOS 7 agora tem a codificação da base 64 nativa (e expõe os métodos anteriormente privados do iOS 4, caso você precise oferecer suporte a versões anteriores do iOS).

Assim, para converter NSDatana NSStringrepresentação base 64, você pode usar base64EncodedStringWithOptions. Se você também tiver que suportar versões do iOS anteriores à 7.0, poderá:

NSString *string;

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

E para converter a base 64 de NSStringvolta para NSDatavocê, você pode usar initWithBase64EncodedString. Da mesma forma, se você precisar oferecer suporte a versões do iOS anteriores à 7.0, poderá:

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, se você não precisa de compatibilidade com versões anteriores do iOS anteriores à 7.0, é ainda mais fácil, basta usar base64EncodedStringWithOptionsou initWithBase64EncodedString, respectivamente, e não se incomodar com a verificação em tempo de execução das versões anteriores do iOS. De fato, se você usar o código acima quando seu destino mínimo for iOS 7 ou superior, receberá um aviso do compilador sobre os métodos descontinuados. Portanto, no iOS 7 e superior, você simplesmente converteria para a base 64 string com:

NSString *string = [data base64EncodedStringWithOptions:kNilOptions];

e volte novamente com:

NSData *data = [[NSData alloc] initWithBase64EncodedString:string options:kNilOptions]; 
Roubar
fonte
Obrigado por isso Rob. Poderia, por favor, explicar brevemente o que escreveu, " ... e expõe os métodos iOS 4 anteriormente privados "?
Phi
8
É uma pena que essa resposta esteja oculta sob todas essas implementações personalizadas. É uma fraqueza do SO, onde uma solução mais apropriada pode ter surgido muito tempo depois que a pergunta original foi feita, essa solução agora tem que competir com o que foi aceito anteriormente.
jakev
É por isso que é sempre útil para upvote mais recentemente respostas corretas :)
Steve Wilford
por que diabos respostas como essa não estão no topo :(, gastei muito tempo processando todas as respostas acima T__T
Alsh compiler
33

O iOS inclui suporte embutido para codificação e decodificação base64. Se você olhar, resolv.hverá as duas funções b64_ntope b64_pton. A biblioteca Square SocketRocket fornece um exemplo razoável de como usar essas funções do objetivo-c.

Essas funções são muito bem testadas e confiáveis ​​- ao contrário de muitas das implementações que você pode encontrar em postagens aleatórias da Internet. Não se esqueça de vincular libresolv.dylib.

quellish
fonte
3
Impressionante; muito melhor do que um site aleatório! Caso alguém esteja preocupado em usar essas funções pouco documentadas, você pode ver a fonte delas no site da Apple .
precisa saber é o seguinte
1
Esse cara fornece um pouco mais de conhecimento sobre o assunto: blog.montgomerie.net/ios-hidden-base64-routines
Mike
21

Como esse parece ser o hit número um do google na codificação base64 e no iphone, tive vontade de compartilhar minha experiência com o snippet de código acima.

Funciona, mas é extremamente lento. Uma referência em uma imagem aleatória (0,4 mb) levou 37 segundos no iphone nativo. O principal motivo é provavelmente toda a mágica do OOP - char NSStrings, etc, que são liberados apenas após a codificação.

Outra sugestão postada aqui (ab) usa a biblioteca openssl, que também parece um exagero.

O código abaixo leva 70 ms - isso é uma aceleração de 500 vezes. Isso apenas codifica a base64 (a decodificação seguirá assim que a encontrar)

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

Eu deixei de fora o corte de linha porque não precisava, mas é trivial acrescentar.

Para quem está interessado em otimizar: o objetivo é minimizar o que acontece no loop principal. Portanto, toda a lógica para lidar com os últimos 3 bytes é tratada fora do loop.

Além disso, tente trabalhar com dados no local, sem copiar adicionalmente para / de buffers. E reduza qualquer aritmética ao mínimo.

Observe que os bits reunidos para procurar uma entrada na tabela não se sobreporiam quando deveriam ser unidos ou trocados. Uma grande melhoria, portanto, poderia ser o uso de 4 tabelas de pesquisa separadas de 256 bytes e eliminar as mudanças, assim:

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

É claro que você poderia ir muito além, mas isso está além do escopo aqui.

mvds
fonte
Hmm. Não consegui fazer isso funcionar. Eu observo uma codificação base64 diferente do meu valor esperado. Você já testou isso com os exemplos na RFC 4648? tools.ietf.org/html/rfc4648
Alex Reynolds
3
Lutando para ver o que base64EncodingTable1, base64EncodingTable2, base64EncodingTable3 e base64EncodingTable4 estão fazendo referência?
Jamie Chapman
Muito útil, mas pode ler além do final do buffer de entrada. Quando (esquerda == 2), bruto [inp + 2] será um byte além do final do tmpbuf. Eu acho que a linha deve ser: if (left == 2) outbuf [outp ++] = base64EncodingTable [((raw [inp + 1] & 0x0F) << 2)];
John Lemberger
altere a seguinte linha <code> char tmpbuf [2] = {0,0}; </code> para <code> char não assinado char tmpbuf [3] = {0,0,0}; </code>
Satya
9

Na excelente melhoria do mvds, existem dois problemas. Altere o código para isso:

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
fonte
9

Melhor solução:

Existe uma função integrada no NSData

[data base64Encoding]; //iOS < 7.0
[data base64EncodedStringWithOptions:NSDataBase64Encoding76CharacterLineLength]; //iOS >= 7.0
Nagaraj
fonte
Podemos fazer isso com base na versão do iOS na qual o aplicativo está sendo executado usando "[[UIDevice currentDevice] systemVersion] .floatValue".
21413 Nagaraj
2
1. Isso não informaria a qual SDK você vinculou, ou seja, uma verificação de tempo de execução. 2. Isso é diretamente contrário às orientações da Apple. Você deve verificar a disponibilidade de um recurso, não a versão do sistema.
quellish
6

Que bom que as pessoas gostaram. O final do jogo foi um pouco defeituoso, devo admitir. Além de definir corretamente inp = 0, você também deve aumentar o tamanho do tmpbuf para 3, como

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

ou deixar de fora o orring de matérias-primas [inp + 2]; se tivéssemos um bruto [inp + 2]! = 0 para este pedaço, ainda estaríamos no circuito, é claro ...

De qualquer maneira, considere manter o bloco de pesquisa da mesa final idêntico ao do loop para maior clareza. Na versão final eu usei eu fiz

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

Para adicionar o ==

Desculpe, eu não verifiquei RFC e outras coisas, deveria ter feito um trabalho melhor!

mvds
fonte
3
você já tem uma conta aqui, pois sua resposta anterior é realmente uma conta diferente. Além disso, isso deve ser uma edição para isso ou um comentário.
Alastair Pitts
@alastair, você parece ter uma "conta" toda vez que postar uma resposta sem se registrar, após limpar os cookies. Não consegui me conectar à minha primeira "conta" (mesmo com o mesmo endereço de e-mail e endereço IP), então apenas a coloquei como uma nova resposta, desculpe por isso. - acabou de se registrar!
Mvds
3
Alguma chance de você poder editar esta resposta na anterior, para que haja uma versão correta definitiva? Obrigado!
Josephh
6

No iOS8 e uso posterior - (NSString *)base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)optionsdo NSData

AlexeyVMP
fonte
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
fonte
2

Aqui está uma versão compacta do Objective-C como uma categoria no NSData. É preciso pensar um pouco ...

@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

O preenchimento pode ser adicionado, se necessário, ampliando o escopo de 'byt' e anexando 'dest' com caracteres de 2 = "" antes de retornar.

Uma categoria pode ser adicionada ao NSString, assim:

@implementation NSString (StringUtils)

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

 return [theData newStringInBase64FromData];
}

@end

fonte
2

O iOS possui métodos de codificação e decodificação Base64 internos (sem usar o libresolv) desde o iOS 4. No entanto, ele foi declarado apenas no iOS 7 SDK. A documentação da Apple indica que você pode usá-lo ao segmentar o iOS 4 e superior.

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

Aqui está um exemplo para converter um objeto NSData na Base 64. Também mostra como seguir o outro caminho (decodifique um objeto NSData codificado na 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];
John Muchow
fonte
1

no iOS 7

        NSData *data=[[NSData alloc]init];
        [data base64Encoding];
Mani
fonte
Nagaraj já mencionou isso. Veja seu post e os comentários que accompnay que estado o seu estado lá desde iOS 4.
JWW
1

Eu fiz isso usando a seguinte classe ..

@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

Ao ligar para a chamada

 [Base64Converter base64StringFromData:dataval length:lengthval];

É isso aí...

Durai Amuthan.H
fonte
1

Eu acho que isso vai 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];

}

Mrug
fonte
NSStringUtil? Por favor, dê uma resposta completa?
Mohsin Khubaib Ahmed
1
Estes são dois métodos que você precisa escrever em qualquer Classe e pode chamá-lo e passar instâncias de String como parâmetro.
Mrug
0

Baixar Base64

Faça o seguinte código para converter uma imagem em base64

NSString *base64String=[UIImagePNGRepresentation(image) base64Encoding];
Pankaj Wadhwa
fonte
0

Conforme sua exigência, criei uma demonstração de amostra usando o Swift 4, na qual você pode codificar / decodificar string e imagem conforme sua exigência.

  • Também adicionei exemplos de métodos de operações 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
        }
    
    
    }
Equipe do iOS
fonte