Bagaimana saya melakukan encoding base64 di iOS?

230

Saya ingin melakukan base64encoding dan decoding, tetapi saya tidak dapat menemukan dukungan dari iPhone SDK. Bagaimana saya bisa melakukan base64encoding dan decoding dengan atau tanpa perpustakaan?

BlueDolphin
sumber
1
Ada contoh kode yang bagus di bagian bawah posting ini. Sangat mandiri ... BaseSixtyFour
Greg Bernhardt
Tautan @GregBernhardt sudah mati.
Cœur

Jawaban:

116

Ini adalah kasus penggunaan yang baik untuk C Objective kategori .

Untuk pengkodean 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

Untuk decoding 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
sumber
5
Jika Obj-C adalah sesuatu seperti C, Anda harus dapat melakukan ini: static char base64EncodingTable [64] = "ABCDE [etc] 789 + /";
Artelius
3
Saya menemukan mengapa saya hanya mendapatkan 4 karakter ... Harus ada a} sebelum kembali untuk while () loop. Saya akan mengeditnya tetapi saya tidak bisa.
Larry Hipp
3
Ini bukan bug penganalisa. Perhatikan kode juga mencoba mengakses inbuf [3] yang berada di luar batas array itu. Kode ini bau.
Mike Weller
1
Apa yang diwakili oleh nilai panjang?
MegaManX
3
Pada iOS7 Apple telah mengekspos metode enkode base 64 asli mereka. Lihat jawaban Rob di bawah ini untuk cara menggunakannya sambil mempertahankan kompatibilitas ke belakang.
Code Commander
100

Implementasi yang benar-benar sangat cepat yang porting (dan dimodifikasi / ditingkatkan) dari perpustakaan PHP Core ke kode Objective-C asli tersedia di Kelas QSStrings dari Perpustakaan QSUtilities . Saya melakukan patokan cepat: file gambar 5,3MB (JPEG) butuh <50ms untuk menyandikan, dan sekitar 140ms untuk mendekode.

Kode untuk seluruh perpustakaan (termasuk Metode Base64) tersedia di GitHub .

Atau sebagai alternatif, jika Anda ingin kode itu hanya untuk metode Base64 sendiri, saya telah mempostingnya di sini:

Pertama, Anda membutuhkan tabel pemetaan:

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

Untuk Menyandikan:

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

Untuk Mendekodekan:

+ (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
sumber
2
Akhirnya implementasi yang benar dan efisien. Terima kasih. Beberapa kode lain di sekitar sini membuatku takut.
Mike Weller
4
Memori yang dialokasikan seperti strResultdalam encoder tampaknya bocor; hanya membutuhkan a free()di akhir (sebelum kembali tetapi setelah NSString stringWithCString)
JosephH
2
Dalam encodeBase64WithData:metode Anda , bukankah parameter pertama dalam panggilan calloc()harus ditambah 1 untuk memperhitungkan null terminator ( '\0') yang Anda tambahkan di akhir?
erikprice
1
Fakta bahwa apel tidak menyediakan ini membuat Tuhan ingin membunuh anak-anak kucing ... banyak dari mereka ...
dsingleton
2
Saya telah menggunakan ini untuk sementara waktu dan tampaknya berfungsi dengan baik sampai saya mulai mendapatkan beberapa kesalahan terkait kerusakan memori dan menggunakan guard malloc saya mempersempitnya ke baris ini: * objPointer = '\ 0'; jadi waspadalah jika Anda menggunakan ini di aplikasi Anda sendiri.
Mattia
72

Secara historis kami akan mengarahkan Anda ke salah satu dari banyak pangkalan 64 perpustakaan pihak ketiga (seperti yang dibahas dalam jawaban lain) untuk mengonversi dari data biner ke basis 64 string (dan kembali), tetapi iOS 7 sekarang memiliki pengkodean base 64 asli (dan memperlihatkan metode iOS 4 yang sebelumnya pribadi, jika Anda perlu mendukung versi iOS sebelumnya).

Dengan demikian untuk mengkonversi NSDatake NSStringbasis 64 representasi yang dapat Anda gunakan base64EncodedStringWithOptions. Jika Anda juga harus mendukung versi iOS sebelum 7.0, Anda dapat melakukannya:

NSString *string;

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

Dan untuk mengonversi basis 64 NSStringkembali ke NSDataAnda dapat menggunakan initWithBase64EncodedString. Demikian juga, jika Anda perlu mendukung versi iOS sebelum 7.0, Anda dapat melakukan:

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
}

Jelas, jika Anda tidak perlu kompatibilitas ke belakang dengan versi iOS sebelum 7.0, itu bahkan lebih mudah, cukup gunakan base64EncodedStringWithOptionsatau initWithBase64EncodedString, masing-masing, dan jangan repot-repot dengan pemeriksaan run-time untuk versi iOS sebelumnya. Bahkan, jika Anda menggunakan kode di atas ketika target minimum Anda adalah iOS 7 atau lebih besar, Anda benar-benar akan mendapatkan peringatan kompiler tentang metode yang sudah usang. Jadi, di iOS 7 dan lebih tinggi, Anda cukup mengkonversi ke basis 64 string dengan:

NSString *string = [data base64EncodedStringWithOptions:kNilOptions];

dan kembali lagi dengan:

NSData *data = [[NSData alloc] initWithBase64EncodedString:string options:kNilOptions]; 
rampok
sumber
Terima kasih tentang Rob itu. Bisakah Anda menjelaskan secara singkat apa yang Anda tulis, " ... dan memaparkan metode iOS 4 yang sebelumnya pribadi "?
phi
8
Sayang sekali jawaban ini terkubur di bawah semua implementasi kustom tersebut. Ini kelemahan SO, di mana solusi yang lebih tepat mungkin muncul lama setelah pertanyaan awal diajukan, solusi itu sekarang harus bersaing dengan apa yang sebelumnya diterima.
jakev
Itulah sebabnya mengapa selalu berguna untuk memperbaiki jawaban yang baru-baru ini benar :)
Steve Wilford
kenapa jawaban seperti ini tidak ada di atas :(, saya menghabiskan banyak waktu memproses semua jawaban di atas T__T
Alsh compiler
33

iOS mencakup dukungan bawaan untuk encoding dan decoding base64. Jika Anda melihat, resolv.hAnda harus melihat dua fungsi b64_ntopdan b64_pton. Pustaka SocketRocket Square menyediakan contoh yang masuk akal tentang bagaimana menggunakan fungsi-fungsi ini dari objektif-c.

Fungsi-fungsi ini cukup teruji dan andal - tidak seperti banyak implementasi yang Anda temukan dalam posting internet acak. Jangan lupa untuk menghubungkan libresolv.dylib.

quellish
sumber
3
Luar biasa; jauh lebih baik daripada situs internet acak! Seandainya ada orang yang khawatir menggunakan fungsi-fungsi yang tidak terdokumentasi ini, Anda dapat melihat sumbernya di situs Apple .
Jesse Rusak
1
Orang ini memberikan latar belakang lebih lanjut tentang itu: blog.montgomerie.net/ios-hidden-base64-routines
Mike
21

Karena ini tampaknya menjadi hit nomor satu google pada encoding base64 dan iphone, saya merasa ingin berbagi pengalaman saya dengan potongan kode di atas.

Ini bekerja, tetapi sangat lambat. Patokan pada gambar acak (0,4 mb) membutuhkan 37 detik pada iphone asli. Alasan utama mungkin semua sihir OOP - single NSStrings dll, yang hanya autoreleased setelah pengkodean dilakukan.

Saran lain yang diposting di sini (ab) menggunakan perpustakaan openssl, yang terasa seperti berlebihan juga.

Kode di bawah ini membutuhkan 70 ms - itu adalah 500 kali percepatan. Ini hanya melakukan encoding base64 (decoding akan segera menyusul setelah saya temui)

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

Saya tidak perlu memotong garis karena saya tidak membutuhkannya, tapi ini sepele untuk ditambahkan.

Bagi mereka yang tertarik untuk mengoptimalkan: tujuannya adalah untuk meminimalkan apa yang terjadi di loop utama. Oleh karena itu semua logika untuk berurusan dengan 3 byte terakhir diperlakukan di luar loop.

Juga, cobalah untuk mengerjakan data di tempat, tanpa menyalin tambahan ke / dari buffer. Dan kurangi aritmatika apa pun seminimal mungkin.

Perhatikan bahwa bit yang disatukan untuk mencari entri di tabel, tidak akan tumpang tindih ketika mereka akan di-orred bersama tanpa bergeser. Perbaikan besar karena itu bisa menggunakan 4 tabel pencarian 256 byte terpisah dan menghilangkan pergeseran, seperti ini:

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

Tentu saja Anda dapat mengambilnya jauh lebih jauh, tapi itu di luar cakupan di sini.

mvds
sumber
Hmm. Saya tidak bisa melakukan ini. Saya mengamati pengkodean base64 yang berbeda dari nilai yang saya harapkan. Sudahkah Anda menguji ini dengan contoh di RFC 4648? tools.ietf.org/html/rfc4648
Alex Reynolds
3
Berjuang untuk melihat apa base64EncodingTable1, base64EncodingTable2, base64EncodingTable3 dan base64EncodingTable4 referensi?
Jamie Chapman
Sangat membantu, tetapi dapat membaca melampaui akhir input buffer. Ketika (kiri == 2), raw [inp + 2] akan menjadi satu byte setelah akhir tmpbuf. Saya pikir barisnya seharusnya: if (left == 2) outbuf [outp ++] = base64EncodingTable [((raw [inp + 1] & 0x0F) << 2)];
John Lemberger
ubah baris berikut <code> char tmpbuf [2] = {0,0}; </code> menjadi <code> char tmpbuf unsigned [3] = {0,0,0}; </code>
Satya
9

Dalam peningkatan mvds yang sangat baik, ada dua masalah. Ubah kode menjadi ini:

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

Solusi yang lebih baik:

Ada fungsi bawaan di NSData

[data base64Encoding]; //iOS < 7.0
[data base64EncodedStringWithOptions:NSDataBase64Encoding76CharacterLineLength]; //iOS >= 7.0
Nagaraj
sumber
Kita dapat melakukannya berdasarkan versi iOS di mana aplikasi berjalan menggunakan "[[UIDevice currentDevice] systemVersion] .floatValue".
Nagaraj
2
1. Itu tidak akan memberi tahu Anda apa SDK yang Anda tautkan, yaitu pemeriksaan runtime. 2. Itu bertentangan langsung dengan panduan Apple. Anda harus memeriksa ketersediaan fitur, bukan versi sistem.
quellish
6

Senang orang menyukainya. Pertandingan akhirnya agak cacat, saya harus mengakui. Selain benar mengatur inp = 0 Anda juga harus meningkatkan ukuran tmpbuf menjadi 3, seperti

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

atau tinggalkan orring mentah [inp + 2]; jika kita memiliki [inp + 2] mentah! = 0 untuk chunk ini, kita masih berada dalam loop, tentu saja ...

Either way bekerja, Anda mungkin mempertimbangkan menjaga blok tabel pencarian akhir identik dengan yang ada di loop untuk kejelasan. Dalam versi final yang saya gunakan saya lakukan

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

Untuk menambahkan ==

Maaf saya tidak memeriksa RFC dan yang lainnya, seharusnya melakukan pekerjaan yang lebih baik!

mvds
sumber
3
Anda sudah memiliki akun di sini, karena jawaban Anda sebelumnya sebenarnya adalah akun yang berbeda. Juga, ini harus berupa edit untuk itu atau komentar.
Alastair Pitts
@alastair, Anda sepertinya mendapatkan "akun" setiap kali Anda mengirim jawaban tanpa mendaftar, setelah membersihkan cookie. Saya tidak dapat terhubung ke "akun" pertama saya (bahkan dengan email dan alamat ip yang sama) jadi saya taruh di sana sebagai jawaban baru, maaf untuk itu. - baru terdaftar!
mvds
3
Apakah Anda dapat mengedit jawaban ini menjadi yang sebelumnya sehingga ada versi yang benar dan pasti? Terima kasih!
JosephH
6

Di bawah iOS8 dan kemudian menggunakan - (NSString *)base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)optionsNSData

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

Berikut ini adalah versi Objective-C yang ringkas sebagai Kategori di NSData. Butuh beberapa pemikiran tentang ...

@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

Padding dapat ditambahkan jika diperlukan dengan membuat lingkup 'byt' lebih luas dan menambahkan 'dest' dengan (2-byt) "=" karakter sebelum kembali.

Kategori kemudian dapat ditambahkan ke NSString, dengan demikian:

@implementation NSString (StringUtils)

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

 return [theData newStringInBase64FromData];
}

@end

sumber
2

iOS telah memiliki metode encoding dan decoding Base64 bawaan (tanpa menggunakan libresolv) sejak iOS 4. Namun, itu hanya dideklarasikan di iOS 7 SDK. Dokumentasi Apple menyatakan bahwa Anda dapat menggunakannya saat menargetkan iOS 4 dan di atasnya.

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

Berikut ini adalah contoh untuk mengkonversi objek NSData ke Base 64. Itu juga menunjukkan cara untuk pergi ke arah lain (mendekode objek NSData yang disandikan basis 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
sumber
1

di iOS 7

        NSData *data=[[NSData alloc]init];
        [data base64Encoding];
Mani
sumber
Nagaraj sudah menyebutkan ini. Lihat posnya dan komentar yang menyertainya yang menyatakan sudah ada di sana sejak iOS 4.
jww
1

Saya telah melakukannya menggunakan kelas berikut ..

@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

Saat memanggil panggilan

 [Base64Converter base64StringFromData:dataval length:lengthval];

Itu dia...

Durai Amuthan.H
sumber
1

Saya pikir ini akan sangat membantu

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

}

Tuan
sumber
NSStringUtil? Tolong beri jawaban yang lengkap?
Mohsin Khubaib Ahmed
1
Ini adalah dua metode yang perlu Anda tulis di Kelas apa pun dan Anda dapat memanggilnya dan melewatkan String instaces sebagai parameter.
Mrug
0

Unduh Base64

Lakukan kode berikut untuk mengonversi gambar ke base64

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

Sesuai kebutuhan Anda, saya telah membuat demo sampel menggunakan Swift 4 di mana Anda dapat menyandikan / mendekode string dan gambar sesuai kebutuhan Anda.

  • Saya juga menambahkan metode sampel operasi yang relevan.

    //
    //  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
        }
    
    
    }
Tim iOS
sumber