Come posso eseguire la codifica Base64 su iOS?


230

Mi piacerebbe fare la base64codifica e la decodifica, ma non sono riuscito a trovare alcun supporto dall'iPhone SDK. Come posso fare la base64codifica e la decodifica con o senza una libreria?


1
C'è un bel esempio di codice in fondo a questo post. Molto autonomo ... BaseSixtyFour
Greg Bernhardt,

Il link @GregBernhardt è morto.
Cœur

Risposte:


116

Si tratta di un caso d'uso buona per l'obiettivo C categorie .

Per la codifica 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

Per la decodifica 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
Se Obj-C è qualcosa di simile a C, dovresti essere in grado di farlo: static char base64EncodingTable [64] = "ABCDE [etc] 789 + /";
Artelius

3
Ho scoperto perché stavo ottenendo solo 4 caratteri ... Ci deve essere un} prima del ritorno per il ciclo while (). Lo modificherei ma non mi sembra possibile.
Larry Hipp,

3
Non è un bug dell'analizzatore. Si noti che il codice tenta anche di accedere a inbuf [3] che è oltre i limiti di tale array. Questo codice puzza.
Mike Weller,

1
Cosa rappresenta il valore della lunghezza?
MegaManX,

3
A partire da iOS7, Apple ha esposto il proprio metodo di codifica 64 di base nativo. Vedi la risposta di Rob qui sotto per come usarla mantenendo la retrocompatibilità.
Codice comandante

100

Un'implementazione davvero molto veloce che è stata trasferita (e modificata / migliorata) dalla libreria PHP Core al codice Objective-C nativo è disponibile nella classe QSStrings dalla libreria QSUtilities . Ho fatto un rapido benchmark: un file di immagine (5,3 MB) da 5,3 MB ha richiesto <50ms per la codifica e circa 140ms per la decodifica.

Il codice per l'intera libreria (inclusi i metodi Base64) sono disponibili su GitHub .

O in alternativa, se vuoi che il codice sia solo per i metodi Base64, l'ho pubblicato qui:

Innanzitutto, sono necessarie le tabelle di mapping:

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

Per codificare:

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

Per decodificare:

+ (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 un'implementazione corretta ed efficiente. Grazie. Alcuni degli altri codici qui intorno mi spaventano.
Mike Weller,

4
La memoria allocata come strResultnell'encoder sembra essere trapelata; ha solo bisogno di un free()alla fine (prima di tornare ma dopo NSString stringWithCString)
JosephH,

2
Nel tuo encodeBase64WithData:metodo, il primo parametro nella chiamata non calloc()deve essere incrementato di 1 per tenere conto del null null ( '\0') che aggiungi alla fine?
erikprice

1
Il fatto che Apple non fornisca questo fa sì che Dio voglia uccidere gattini ... molti di loro ...
Dsingleton,

2
Lo sto usando da un po 'e mi è sembrato che funzionasse benissimo fino a quando ho iniziato a ricevere alcuni errori relativi alla corruzione della memoria e usando Guard Malloc l'ho ridotto a questa riga: * objPointer =' \ 0 '; quindi fai attenzione se lo usi nelle tue app.
Mattia,

72

Storicamente ti avremmo indirizzato a una delle molte librerie di base 64 di terze parti (come discusso nelle altre risposte) per la conversione da dati binari a stringa di base 64 (e ritorno), ma iOS 7 ora ha la codifica di base 64 nativa (e espone i metodi iOS 4 precedentemente privati, nel caso in cui sia necessario supportare versioni precedenti di iOS).

Quindi, per convertire NSDatain NSStringrappresentazione 64 base è possibile utilizzare base64EncodedStringWithOptions. Se devi supportare anche versioni iOS precedenti alla 7.0, puoi fare:

NSString *string;

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

E alla base convertito 64 NSStringdi nuovo a NSDataè possibile utilizzare initWithBase64EncodedString. Allo stesso modo, se devi supportare versioni iOS precedenti alla 7.0, puoi fare:

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
}

Ovviamente, se non hai bisogno di retrocompatibilità con le versioni di iOS precedenti alla 7.0, è ancora più semplice, basta usare base64EncodedStringWithOptions o initWithBase64EncodedString, rispettivamente, e non preoccuparti del controllo del runtime per le versioni precedenti di iOS. In effetti, se usi il codice sopra quando il tuo target minimo è iOS 7 o superiore, riceverai effettivamente un avviso del compilatore sui metodi obsoleti. Quindi, in iOS 7 e versioni successive, convertiresti semplicemente nella stringa di base 64 con:

NSString *string = [data base64EncodedStringWithOptions:kNilOptions];

e di nuovo con:

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

Grazie a quel Rob. Potresti per favore approfondire brevemente ciò che hai scritto, " ... ed esporre i metodi iOS 4 precedentemente privati "?
phi,

8
È un peccato che questa risposta sia sepolta sotto tutte quelle implementazioni personalizzate. È un punto debole della SO, in cui una soluzione più appropriata potrebbe essere arrivata molto tempo dopo la domanda iniziale, quella soluzione ora deve competere con ciò che era stato precedentemente accettato.
Jakev,

Ecco perché è sempre utile valutare le risposte corrette più recenti :)
Steve Wilford,

perché diavolo le risposte in questo modo non sono in cima :(, ho passato molto tempo a elaborare tutte le risposte sopra T__T
Alsh compilatore

33

iOS include il supporto integrato per la codifica e decodifica base64. Se guardi resolv.hdovresti vedere le due funzioni b64_ntope b64_pton. La libreria Square SocketRocket fornisce un ragionevole esempio di come utilizzare queste funzioni da goal -c.

Queste funzioni sono abbastanza ben testate e affidabili - a differenza di molte delle implementazioni che potresti trovare in postazioni internet casuali. Non dimenticare di collegarti contro libresolv.dylib.


3
Eccezionale; molto meglio di un sito internet casuale! Nel caso in cui qualcuno sia preoccupato dall'uso di queste funzioni scarsamente documentate, puoi vedere la fonte per queste sul sito di Apple .
Jesse Rusak,

1
Questo ragazzo ci dà un po 'di più su di esso: blog.montgomerie.net/ios-hidden-base64-routines
Mike

21

Dato che questo sembra essere il numero uno di Google nella codifica Base64 e iPhone, mi è venuta voglia di condividere la mia esperienza con lo snippet di codice sopra.

Funziona, ma è estremamente lento. Un benchmark su un'immagine casuale (0,4 mb) ha richiesto 37 secondi su iPhone nativo. Il motivo principale è probabilmente tutta la magia OOP - NSStrings a carattere singolo, ecc., Che vengono rilasciati automaticamente solo dopo aver eseguito la codifica.

Un altro suggerimento pubblicato qui (ab) utilizza la libreria openssl, che sembra eccessiva.

Il codice seguente richiede 70 ms, ovvero una velocità di 500 volte. Questo fa solo la codifica base64 (la decodifica seguirà non appena la incontrerò)

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

Ho lasciato fuori il taglio della linea poiché non ne avevo bisogno, ma è banale aggiungere.

Per coloro che sono interessati all'ottimizzazione: l'obiettivo è ridurre al minimo ciò che accade nel ciclo principale. Pertanto, tutta la logica per gestire gli ultimi 3 byte viene trattata al di fuori del ciclo.

Inoltre, prova a lavorare sui dati sul posto, senza ulteriori copie da / verso i buffer. E ridurre qualsiasi aritmetica al minimo indispensabile.

Osserva che i bit che sono messi insieme per cercare una voce nella tabella, non si sovrapporrebbero quando dovevano essere rigettati senza spostare. Un grande miglioramento potrebbe quindi essere l'uso di 4 tabelle di ricerca separate da 256 byte ed eliminare i turni, in questo modo:

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

Ovviamente potresti andare molto oltre, ma qui va oltre lo scopo.


Hmm. Non sono riuscito a farlo funzionare. Osservo una codifica Base64 diversa rispetto al mio valore previsto. Hai provato questo con gli esempi in RFC 4648? tools.ietf.org/html/rfc4648
Alex Reynolds

3
Lottando per vedere a cosa fanno riferimento base64EncodingTable1, base64EncodingTable2, base64EncodingTable3 e base64EncodingTable4?
Jamie Chapman,

Molto utile, ma può leggere oltre la fine del buffer di input. Quando (left == 2), raw [inp + 2] sarà un byte oltre la fine di tmpbuf. Penso che la linea dovrebbe essere: if (left == 2) outbuf [outp ++] = base64EncodingTable [((raw [inp + 1] & 0x0F) << 2)];
John Lemberger,

cambia la seguente riga <code> char tmpbuf [2] = {0,0}; </code> in <code> unsigned char tmpbuf [3] = {0,0,0}; </code>
Satya

9

Nell'ottimo miglioramento di mvds, ci sono due problemi. Cambia codice in questo:

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

Soluzione migliore:

C'è una funzione integrata in NSData

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

Possiamo farlo in base alla versione iOS su cui è in esecuzione l'app utilizzando "[[UIDevice currentDevice] systemVersion] .floatValue".
Nagaraj,

2
1. Questo non ti direbbe a quale SDK hai collegato, questo è un controllo di runtime. 2. Ciò è direttamente contrario alla guida di Apple. Dovresti verificare la disponibilità di una funzionalità, non la versione del sistema.
Quellish

6

Alla gente è piaciuto. La fine del gioco è stata un po 'imperfetta, devo ammetterlo. Oltre a impostare correttamente inp = 0 dovresti anche aumentare le dimensioni di tmpbuf a 3, come

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

o tralasciare l'orring di raw [inp + 2]; se avessimo un raw [inp + 2]! = 0 per questo pezzo saremmo ancora nel ciclo ovviamente ...

Ad ogni modo, potresti considerare di mantenere il blocco di ricerca del tavolo finale identico a quello nel loop per chiarezza. Nella versione finale che ho usato l'ho fatto

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

Per aggiungere il ==

Mi dispiace di non aver controllato RFC e roba del genere, avrei dovuto fare un lavoro migliore!


3
hai già un account qui, poiché la tua risposta precedente è in realtà un account diverso. Inoltre, questa dovrebbe essere una modifica o un commento.
Alastair Pitts,

@alastair, sembra che tu abbia un "account" ogni volta che pubblichi una risposta senza registrarti, dopo aver pulito i cookie. Non sono stato in grado di connettermi al mio primo "account" (anche con lo stesso indirizzo e-mail e indirizzo IP), quindi l'ho appena inserito come una nuova risposta, mi dispiace per quello. - appena registrato!
mvds

3
Qualche possibilità che tu possa modificare questa risposta nella tua precedente, quindi esiste una versione corretta definitiva? Grazie!
JosephH,

6

Sotto iOS8 e uso successivo - (NSString *)base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)optionsdi 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

Ecco una versione compatta di Objective-C come categoria su NSData. Ci vuole un po 'a pensare a ...

@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

Il riempimento può essere aggiunto, se necessario, allargando l'ambito di 'byt' e aggiungendo 'dest' con (2-byt) caratteri "=" prima di tornare.

Una categoria può quindi essere aggiunta a NSString, quindi:

@implementation NSString (StringUtils)

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

 return [theData newStringInBase64FromData];
}

@end

2

iOS ha avuto metodi di codifica e decodifica Base64 integrati (senza usare libresolv) da iOS 4. Tuttavia, è stato dichiarato solo nell'SDK di iOS 7. La documentazione di Apple afferma che è possibile utilizzarlo quando si sceglie come target iOS 4 e versioni successive.

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

2

Ecco un esempio per convertire un oggetto NSData in Base 64. Viene inoltre illustrato come procedere in altro modo (decodificare un oggetto NSData con codifica 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

in iOS 7

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

Nagaraj lo ha già menzionato. Vedi il suo post e i commenti che lo accompagnano che affermano che è stato lì da iOS 4.
jww

1

L'ho fatto usando la seguente 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

Durante la chiamata

 [Base64Converter base64StringFromData:dataval length:lengthval];

Questo è tutto...


1

Penso che questo sarà utile

 + (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? Per favore, dai una risposta completa?
Mohsin Khubaib Ahmed,

1
Questi sono due metodi che devi scrivere in qualsiasi classe e puoi chiamarlo e passare istanze String come parametro.
Mrug,

0

Scarica Base64

Eseguire il codice seguente per convertire un'immagine in base64

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

0

In base alle tue esigenze ho creato una demo di esempio utilizzando Swift 4 in cui puoi codificare / decodificare stringhe e immagini secondo le tue esigenze.

  • Ho anche aggiunto metodi di esempio delle operazioni pertinenti.

    //
    //  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
        }
    
    
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.