Làm cách nào để mã hóa base64 trên iOS?


230

Tôi muốn thực hiện base64mã hóa và giải mã, nhưng tôi không thể tìm thấy bất kỳ sự hỗ trợ nào từ iPhone SDK. Làm cách nào để base64mã hóa và giải mã có hoặc không có thư viện?


1
Có một mẫu mã đẹp ở cuối bài này. Rất khép kín ... BaseSixtyFour
Greg Bernhardt

Liên kết @GregBernhardt đã chết.
Cœur

Câu trả lời:


116

Đây là một trường hợp sử dụng tốt cho C Mục tiêu loại .

Đối với mã hóa 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

Đối với giải mã 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
Nếu Obj-C là bất cứ thứ gì giống như C, bạn sẽ có thể làm điều này: static char base64EncodingTable [64] = "ABCDE [etc] 789 + /";
Artelius

3
Tôi đã tìm thấy lý do tại sao tôi chỉ nhận được 4 ký tự ... Cần phải có một} trước khi trả về vòng lặp while (). Tôi sẽ chỉnh sửa nó nhưng tôi không giống như tôi có thể.
Larry Hipp

3
Đây không phải là lỗi phân tích. Lưu ý rằng mã cũng cố gắng truy cập inbuf [3] nằm ngoài giới hạn của mảng đó. Mã này bốc mùi.
Mike Weller

1
Giá trị chiều dài đại diện cho cái gì?
MegaManX

3
Kể từ iOS7, Apple đã tiết lộ phương pháp mã hóa cơ sở 64 gốc của họ. Xem câu trả lời của Rob bên dưới để biết cách sử dụng nó trong khi duy trì khả năng tương thích ngược.
Chỉ huy mã

100

Việc triển khai thực sự rất nhanh, được chuyển (và sửa đổi / cải tiến) từ thư viện PHP Core thành mã Objective-C gốc có sẵn trong Lớp QSStrings từ Thư viện QSUtilities . Tôi đã thực hiện một điểm chuẩn nhanh: một tệp hình ảnh 5,3 MB (JPEG) mất <50ms để mã hóa và khoảng 140ms để giải mã.

Mã cho toàn bộ thư viện (bao gồm Phương thức Base64) có sẵn trên GitHub .

Hoặc cách khác, nếu bạn muốn mã chỉ là các phương thức Base64, tôi đã đăng nó ở đây:

Đầu tiên, bạn cần các bảng ánh xạ:

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

Để mã hoá:

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

Để giải mã:

+ (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
Cuối cùng là một thực hiện chính xác và hiệu quả. Cảm ơn. Một số mã khác ở đây làm tôi sợ.
Mike Weller

4
Bộ nhớ được phân bổ như strResulttrong bộ mã hóa dường như bị rò rỉ; nó chỉ cần một free()cái kết thúc (trước khi trở về nhưng sau đó NSString stringWithCString)
JosephH

2
Trong encodeBase64WithData:phương thức của bạn , không phải là tham số đầu tiên trong lệnh gọi calloc()cần được tăng thêm 1 để tính đến bộ kết thúc null ( '\0') mà bạn thêm vào cuối?
erikprice

1
Việc táo không cung cấp điều này khiến Chúa muốn giết mèo con ... rất nhiều trong số chúng ...
DSingleton

2
Tôi đã sử dụng điều này được một thời gian và nó dường như hoạt động rất tốt cho đến khi tôi bắt đầu gặp một số lỗi liên quan đến hỏng bộ nhớ và sử dụng bảo vệ malloc Tôi đã thu hẹp nó xuống dòng này: * objPulum = '\ 0'; vì vậy hãy cẩn thận nếu bạn sử dụng điều này trong các ứng dụng của riêng bạn.
Mattia

72

Trong lịch sử, chúng tôi đã hướng bạn đến một trong nhiều thư viện 64 cơ sở của bên thứ ba (như đã thảo luận trong các câu trả lời khác) để chuyển đổi từ dữ liệu nhị phân sang chuỗi 64 cơ sở (và trở lại), nhưng iOS 7 hiện có mã hóa cơ sở 64 gốc (và hiển thị các phương thức iOS 4 riêng tư trước đây, trong trường hợp bạn cần hỗ trợ các phiên bản iOS cũ hơn).

Do đó, để chuyển đổi NSDatasang NSStringđại diện cơ sở 64, bạn có thể sử dụng base64EncodedStringWithOptions. Nếu bạn cũng phải hỗ trợ các phiên bản iOS trước 7.0, bạn có thể làm:

NSString *string;

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

Và để chuyển đổi cơ sở 64 NSStringtrở lại, NSDatabạn có thể sử dụng initWithBase64EncodedString. Tương tự, nếu bạn cần hỗ trợ các phiên bản iOS trước 7.0, bạn có thể làm:

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
}

Rõ ràng, nếu bạn không cần khả năng tương thích ngược với các phiên bản iOS trước 7.0, thì thậm chí còn dễ dàng hơn, chỉ cần sử dụng base64EncodedStringWithOptionshoặc initWithBase64EncodedString, và không bận tâm đến việc kiểm tra thời gian chạy cho các phiên bản iOS trước đó. Thực tế, nếu bạn sử dụng mã trên khi mục tiêu tối thiểu của bạn là iOS 7 trở lên, bạn sẽ thực sự nhận được cảnh báo về trình biên dịch về các phương thức không dùng nữa. Vì vậy, trong iOS 7 trở lên, bạn chỉ cần chuyển đổi sang chuỗi 64 cơ sở với:

NSString *string = [data base64EncodedStringWithOptions:kNilOptions];

và quay lại lần nữa với:

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

Cảm ơn về điều đó Rob. Bạn có thể vui lòng giải thích ngắn gọn về những gì bạn đã viết, " ... và hiển thị các phương thức iOS 4 riêng tư trước đây " không?
phi

8
Thật xấu hổ khi câu trả lời này bị chôn vùi dưới tất cả những triển khai tùy chỉnh đó. Đó là một điểm yếu của SO, nơi một giải pháp phù hợp hơn có thể đã xuất hiện từ lâu sau khi câu hỏi ban đầu được hỏi, giải pháp đó giờ phải cạnh tranh với những gì đã được chấp nhận trước đây.
jakev

Đó là lý do tại sao nó luôn hữu ích để nâng cao câu trả lời gần đây hơn :)
Steve Wilford

Tại sao câu trả lời quái quỷ như thế này lại không đứng đầu :(, tôi đã dành rất nhiều thời gian để xử lý tất cả các câu trả lời ở trên T__T
Trình biên dịch Alsh

33

iOS bao gồm hỗ trợ tích hợp cho mã hóa và giải mã base64. Nếu bạn nhìn vào resolv.hbạn sẽ thấy hai chức năng b64_ntopb64_pton. Thư viện Square SocketRocket cung cấp một ví dụ hợp lý về cách sử dụng các hàm này từ object -c.

Các chức năng này được kiểm tra khá tốt và đáng tin cậy - không giống như nhiều triển khai bạn có thể tìm thấy trong các bài đăng ngẫu nhiên trên internet. Đừng quên liên kết với libresolv.dylib.


3
Tuyệt vời; tốt hơn nhiều so với trang web ngẫu nhiên Trong trường hợp bất kỳ ai lo lắng về việc sử dụng các chức năng được ghi chép lại này, bạn có thể xem nguồn cho các chức năng này trên trang web của Apple .
Jesse Rusak

1
Anh chàng này cung cấp thêm một số nền tảng về nó: blog.monteimerie.net/ios-hidden-base64-routines
Mike

21

Vì đây có vẻ là số một google đánh vào mã hóa base64 và iphone, tôi cảm thấy muốn chia sẻ trải nghiệm của mình với đoạn mã ở trên.

Nó hoạt động, nhưng nó cực kỳ chậm. Một điểm chuẩn trên một hình ảnh ngẫu nhiên (0,4 mb) mất 37 giây trên iphone gốc. Lý do chính có lẽ là tất cả các phép thuật OOP - char NSStrings đơn, v.v., chỉ được tự động phát hành sau khi mã hóa được thực hiện.

Một đề nghị khác được đăng ở đây (ab) sử dụng thư viện openssl, cảm giác như quá mức cần thiết.

Mã dưới đây mất 70 ms - đó là một lần tăng tốc 500 lần. Điều này chỉ thực hiện mã hóa base64 (giải mã sẽ theo sau khi tôi gặp nó)

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

Tôi đã bỏ qua việc cắt đường vì tôi không cần nó, nhưng thật tầm thường khi thêm vào.

Đối với những người quan tâm đến việc tối ưu hóa: mục tiêu là giảm thiểu những gì xảy ra trong vòng lặp chính. Do đó, tất cả logic để xử lý 3 byte cuối cùng được xử lý bên ngoài vòng lặp.

Ngoài ra, hãy cố gắng làm việc trên dữ liệu tại chỗ, mà không cần sao chép bổ sung vào / từ bộ đệm. Và giảm bất kỳ số học đến mức tối thiểu.

Quan sát rằng các bit được đặt cùng nhau để tìm kiếm một mục trong bảng, sẽ không trùng lặp khi chúng được ghép với nhau mà không dịch chuyển. Do đó, một cải tiến lớn có thể là sử dụng 4 bảng tra cứu 256 byte riêng biệt và loại bỏ các ca, như thế này:

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

Tất nhiên bạn có thể đưa nó đi xa hơn nhiều, nhưng điều đó vượt quá phạm vi ở đây.


Hừm. Tôi không thể làm việc này. Tôi quan sát một mã hóa base64 khác với giá trị mong đợi của tôi. Bạn đã thử nghiệm điều này với các ví dụ trong RFC 4648 chưa? tools.ietf.org/html/rfc4648
Alex Reynold

3
Đấu tranh để xem những gì base64EncodingTable1, base64EncodingTable2, base64EncodingTable3 và base64EncodingTable4 đang tham chiếu?
Jamie Chapman

Rất hữu ích, nhưng nó có thể đọc vượt quá phần cuối của bộ đệm đầu vào. Khi (left == 2), raw [inp + 2] sẽ là một byte vượt quá cuối tmpbuf. Tôi nghĩ dòng nên là: if (left == 2) outbuf [outp ++] = base64EncodingTable [((raw [inp + 1] & 0x0F) << 2)];
John Lemberger

thay đổi dòng sau <code> char tmpbuf [2] = {0,0}; </ code> thành <code> unsign char tmpbuf [3] = {0,0,0}; </ code>
Satya

9

Trong cải tiến tuyệt vời của mvds, có hai vấn đề. Thay đổi mã thành này:

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

Giải pháp tốt hơn :

Có một chức năng tích hợp trong NSData

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

Chúng tôi có thể làm điều đó dựa trên phiên bản iOS mà ứng dụng đang chạy bằng cách sử dụng "[[UIDevice currentDevice] systemVersion] .floatValue".
Nagaraj

2
1. Điều đó sẽ không cho bạn biết SDK bạn đã liên kết với cái gì, đó là kiểm tra thời gian chạy. 2. Điều đó trực tiếp với hướng dẫn của Apple. Bạn nên kiểm tra tính khả dụng của một tính năng, không phải phiên bản hệ thống.
quellish

6

Vui mừng mọi người thích nó. Trò chơi kết thúc có một chút thiếu sót tôi phải thừa nhận. Bên cạnh việc đặt đúng inp = 0, bạn cũng nên tăng kích thước của tmpbuf lên 3, như

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

hoặc bỏ qua orring của raw [inp + 2]; nếu chúng ta có [inp + 2]! = 0 cho đoạn này, chúng ta vẫn sẽ ở trong vòng lặp ...

Dù bằng cách nào, bạn có thể cân nhắc giữ cho khối tra cứu bảng cuối cùng giống hệt với khối trong vòng lặp cho rõ ràng. Trong phiên bản cuối cùng tôi đã sử dụng tôi đã làm

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

Để thêm ==

Xin lỗi tôi đã không kiểm tra RFC và các công cụ, nên đã hoàn thành công việc tốt hơn!


3
bạn đã có một tài khoản ở đây, vì câu trả lời trước của bạn thực sự là một tài khoản khác. Ngoài ra, đây nên là một chỉnh sửa cho điều đó hoặc một nhận xét.
Alastair Forge

@alastair, bạn dường như nhận được "tài khoản" mỗi khi bạn đăng câu trả lời mà không cần đăng ký, sau khi làm sạch cookie. Tôi không thể kết nối với "tài khoản" đầu tiên của mình (ngay cả với cùng một địa chỉ email và địa chỉ IP) vì vậy tôi chỉ đặt nó ở đó như một câu trả lời mới, xin lỗi vì điều đó. - vừa đăng ký!
mvds

3
Bất kỳ cơ hội nào bạn có thể chỉnh sửa câu trả lời này thành câu trả lời trước để có phiên bản chính xác? Cảm ơn!
JosephH

6

Trong iOS8 và sau đó sử dụng - (NSString *)base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)optionsNSData


3
#import "NSDataAdditions.h"
@implementation NSData (NSDataAdditions)

+ (NSData *) base64DataFromString: (NSString *)string {
  unsigned long ixtext, lentext;
  unsigned char ch, input[4], output[3];
  short i, ixinput;
  Boolean flignore, flendtext = false;
  const char *temporary;
  NSMutableData *result;

  if (!string)
    return [NSData data];

  ixtext = 0;
  temporary = [string UTF8String];
  lentext = [string length];
  result = [NSMutableData dataWithCapacity: lentext];
  ixinput = 0;

  while (true) {
    if (ixtext >= lentext)
      break;
    ch = temporary[ixtext++];
    flignore = false;

    if ((ch >= 'A') && (ch <= 'Z'))
      ch = ch - 'A';
    else if ((ch >= 'a') && (ch <= 'z'))
      ch = ch - 'a' + 26;
    else if ((ch >= '0') && (ch <= '9'))
      ch = ch - '0' + 52;
    else if (ch == '+')
      ch = 62;
    else if (ch == '=')
      flendtext = true;
    else if (ch == '/')
      ch = 63;
    else
      flignore = true;

    if (!flignore) {
      short ctcharsinput = 3;
      Boolean flbreak = false;

      if (flendtext) {
         if (ixinput == 0)
           break;              
         if ((ixinput == 1) || (ixinput == 2))
           ctcharsinput = 1;
         else
           ctcharsinput = 2;
         ixinput = 3;
         flbreak = true;
      }

      input[ixinput++] = ch;

      if (ixinput == 4){
        ixinput = 0;
        output[0] = (input[0] << 2) | ((input[1] & 0x30) >> 4);
        output[1] = ((input[1] & 0x0F) << 4) | ((input[2] & 0x3C) >> 2);
        output[2] = ((input[2] & 0x03) << 6) | (input[3] & 0x3F);
        for (i = 0; i < ctcharsinput; i++)
        [result appendBytes: &output[i] length: 1];
      }
    if (flbreak)
      break;
    }
  }
  return result;
}
@end


2

Đây là phiên bản Objective-C nhỏ gọn dưới dạng Danh mục trên NSData. Phải suy nghĩ về ...

@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

Có thể thêm phần đệm nếu được yêu cầu bằng cách làm cho phạm vi của 'byt' rộng hơn và nối thêm 'mệnh' với các ký tự (2-byt) "=" trước khi quay lại.

Một Danh mục sau đó có thể được thêm vào NSString, do đó:

@implementation NSString (StringUtils)

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

 return [theData newStringInBase64FromData];
}

@end

2

iOS đã có các phương thức mã hóa và giải mã Base64 tích hợp (không sử dụng libresolv) kể từ iOS 4. Tuy nhiên, nó chỉ được khai báo trong SDK iOS 7. Tài liệu của Apple tuyên bố rằng bạn có thể sử dụng nó khi nhắm mục tiêu iOS 4 trở lên.

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

2

Dưới đây là một ví dụ để chuyển đổi một đối tượng NSData sang Base 64. Nó cũng cho thấy cách đi theo cách khác (giải mã một đối tượng NSData được mã hóa 64 cơ sở):

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

trong iOS 7

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

Nagaraj đã đề cập đến điều này. Xem bài đăng của anh ấy và các bình luận đi kèm với nó nói rằng nó đã ở đó kể từ iOS 4.
jww

1

Tôi đã thực hiện nó bằng cách sử dụng lớp sau ..

@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

Trong khi gọi cuộc gọi

 [Base64Converter base64StringFromData:dataval length:lengthval];

Đó là nó...


1

Tôi nghĩ rằng điều này sẽ hữu ích

 + (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? Hãy cho một câu trả lời đầy đủ?
Mohsin Khubaib Ahmed

1
Đây là hai phương thức bạn cần viết trong bất kỳ Lớp nào và bạn có thể gọi nó và truyền các chuỗi ký tự dưới dạng tham số.
Mrug

0

Tải xuống Base64

Thực hiện theo mã để chuyển đổi một hình ảnh sang cơ sở64

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

0

Theo yêu cầu của bạn, tôi đã tạo một bản demo mẫu bằng Swift 4, trong đó bạn có thể mã hóa / giải mã chuỗi và hình ảnh theo yêu cầu của bạn.

  • Tôi cũng đã thêm các phương pháp mẫu của các hoạt động liên quan.

    //
    //  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
        }
    
    
    }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.