使用SOAP的WCF服务从iPhone上传图像到服务器

3

你好,我正在使用WCF SOAP服务开发我的应用程序,并向服务器发送以下请求:

postStr = [NSString stringWithFormat:@"<?xml version=\"1.0\"?>\n"
           "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"
           "<s:Body>\n"
           "<InsUpdDelActivityInfo xmlns=\"http://tempuri.org/\">\n"
           "<objEventsContent xmlns:d4p1=\"http://schemas.datacontract.org/2004/07/iCampuslite.Model.ActivityStream\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">\n"
           "<d4p1:ActCommentId i:nil=\"true\" />\n"
           "<d4p1:ActSubTypeCd i:nil=\"true\" />\n"
           "<d4p1:ActType>Status</d4p1:ActType>\n"
           "<d4p1:ActTypeCd>1</d4p1:ActTypeCd>\n"
           "<d4p1:ActivityAnswersList />\n"
           "<d4p1:ActivityComments />\n"
           "<d4p1:ActivityId i:nil=\"true\" />\n"
           "<d4p1:ActivityLike />\n"
           "<d4p1:ActivityName>%@</d4p1:ActivityName>\n"
           "<d4p1:ActivityStreamImagesBytes>%@</d4p1:ActivityStreamImagesBytes>\n"
           "<d4p1:AnswerDesc i:nil=\"true\" />\n"
           "<d4p1:AnswerId>0</d4p1:AnswerId>\n"
           "<d4p1:CommentDesc i:nil=\"true\" />\n"
           "<d4p1:CommentId i:nil=\"true\" />\n"
           "<d4p1:CreatedUserId>%@</d4p1:CreatedUserId>\n"
           "<d4p1:CreatedUserName i:nil=\"true\" />\n"
           "<d4p1:FileOrLinkName i:nil=\"true\" />\n"
           "<d4p1:IsLiked>0</d4p1:IsLiked>\n"
           "<d4p1:IsTotalSchool i:nil=\"true\" />\n"
           "<d4p1:LikeCount>0</d4p1:LikeCount>\n"
           "<d4p1:LinkImage i:nil=\"true\" />\n"
           "<d4p1:ObjTypeCdId i:nil=\"true\" />\n"
           "<d4p1:ObjTypeId i:nil=\"true\" />\n"
           "<d4p1:OperationMode>I</d4p1:OperationMode>\n"
           "<d4p1:OperationType i:nil=\"true\" />\n"
           "<d4p1:OperationTypeId i:nil=\"true\" />\n"
           "<d4p1:OrganizationId>%@</d4p1:OrganizationId>\n"
           "<d4p1:OtherActivityId i:nil=\"true\" />\n"
           "%@\n"
           "</objEventsContent>\n"
           "<ismobile>true</ismobile>\n"
           "</InsUpdDelActivityInfo>\n"
           "</s:Body>\n"
           "</s:Envelope>", statusText, [appDelegateObj.loginUserInfoDict valueForKey:@"a:UserId"], [appDelegateObj.loginUserInfoDict valueForKey:@"a:OrgId"], workspaceStr];

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@iCampusliteMobileService/ActivityStreamSl.svc", appDelegateObj.baseURL]]];
NSString *messageLength = [NSString stringWithFormat:@"%lu", (unsigned long)[postStr length]];
[request addValue:@"text/xml; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
[request addValue:@"http://tempuri.org/IActivityStreamSl/InsUpdDelActivityInfo" forHTTPHeaderField:@"SOAPAction"];
[request addValue:messageLength forHTTPHeaderField:@"Content-Length"];
[request setHTTPMethod:@"POST"];
[request setHTTPBody:[postStr dataUsingEncoding:NSUTF8StringEncoding]];
NSError *error = nil;
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&error];

在上述请求中,有一个元素ActivityStreamImagesBytes,它是一个base64binary参数,我必须传递图片。
我尝试了许多不同的格式。
这是WCF服务的截图 here 这是服务器端的代码。
public string byteArrayToImage(byte[] byteArrayIn,string fileName)
    {
        if (byteArrayIn != null)
        {
            ActivityStreamSl.LogMsg("Byte Array Count : "+byteArrayIn.Length.ToString(), "D:\\log.txt");
            var serverfile = "D:somepath\somepath\somefolder";
            var getfile = HelperClass.Filesavehelper(Constants.UploadPaths.ActivityStream, "testfilename.png", serverfile);

            FileStream file = new FileStream(getfile, FileMode.Create);
            file.Write(byteArrayIn, 0, byteArrayIn.Length);
            file.Close();
            file.Dispose();
            return getfile;
        }
        else
        {
            ActivityStreamSl.LogMsg("Byte array is null","D:\\log.txt");
        }
        return "";
    }

服务器期望一个字节数组,我不知道该如何发送它?并且我不知道什么是base64binary数据类型?我需要发送base64编码的字符串、字节数组还是只需要从NSData *data = UIImageJPEGRepresentation([UIImage imageNamed:@"popular.png"], 0.7);中获取图像数据即可?
非常感谢任何帮助。

你看过这个问题吗?https://dev59.com/FHVD5IYBdhLWcg3wU56G 按照它的说法,你需要使用Base64编码来对二进制数据进行编码,然后将一个属性dt:dt="binary.base64"添加到节点中。顺便说一下,在这种情况下,你可以直接使用[NSData dataWithContentsOfFile:]来获取图像数据。 - Yarlik
2个回答

2
WCF服务明确要求使用base64binary,因此您必须仅以base64binary格式发送它。 base64binary实际上是base64编码的二进制数据
1)使用以下代码片段将图像转换为二进制。
NSData *binaryImageData  = UIImageJPEGRepresentation([UIImage imageNamed:@"Photo.png"], 0.0);

2)将二进制编码为base64格式

这是我用来处理base64二进制和UIImage的类:

 @interface base64BinaryandImagehandler:NSObject
   +(NSString *) base64BinaryStringFromBinaryData: (NSData *)data length: (int)length;
   +(UIImage*)Base64BinaryToImage:(NSString *)Base64;
 @end


 @implementation base64BinaryandImagehandler:NSObject
      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 *) base64BinaryStringFromBinaryData: (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;
      }

  +(UIImage*)Base64BinaryToImage:(NSString *)Base64{      


        NSString* base64String=[[NSString alloc]initWithFormat:@"data:image/png;base64,%@",Base64];

       NSURL *base64url = [NSURL URLWithString:base64String];

       NSData *imageData = [NSData dataWithContentsOfURL:base64url];

       UIImage *img=[UIImage imageWithData:imageData];

      return img;
     }
 @end 

这里是将二进制转换为base64二进制的方法。
NSString *Base64Binary=[base64BinaryandImagehandler base64BinaryStringFromBinaryData:binaryImageData length:binaryImageData.length];

您可以在这里找到更多信息。
以下是C#中的Base64编码和解码部分。
public static string Base64Encode(string plainText) {
  var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
  return System.Convert.ToBase64String(plainTextBytes);
}

public static string Base64Decode(string base64EncodedData) {
  var base64EncodedBytes = System.Convert.FromBase64String(base64EncodedData);
  return System.Text.Encoding.UTF8.GetString(base64EncodedBytes);
}

无论如何,您可以忽略C#部分,因为您只需要进行iPhone部分。
您可以使用以下链接测试编码的base64是否正确。 base64toimage转换器 更新1 这是确认接收到的字符串是否为base64格式的代码。
   private bool IsBase64String(string str){
    try{
    // If not exception is caught, then it is a base64 string
    MemoryStream stream = new MemoryStream(Convert.FromBase64String(str));
    return true;
    }
    catch{
    // If exception is caught, then I assumed it is a normal string
    return false;
    }
    }

参考资料

以下代码将创建一个图像,位于Images文件夹下,用于给定的到达base64数据和您选择的文件名(例如duraiamuthan.jpg),并返回图像的路径,以便您可以在数据库中更新它。 (它将检查到达的数据是base64还是二进制[以防万一WCF框架在内部将base64转换为二进制])

  public string Getpath_CreateImageFromEncodedData(string Base64EncodedData, string fileName)
        {
        string fileHandle = "";
        try
        {
            if(IsBase64String(Base64EncodedData))
            byte[] imageBytes = Convert.FromBase64String(Base64EncodedData);
            else
            byte[] imageBytes= Base64EncodedData;

            MemoryStream ms = new MemoryStream(imageBytes, 0, imageBytes.Length);

            ms.Write(imageBytes, 0, imageBytes.Length);

            fileHandle = System.Web.HttpContext.Current.Server.MapPath("Images/" + fileName);

            FileStream fsObj = new FileStream(fileHandle, FileMode.Create, FileAccess.Write);

            ms.WriteTo(fsObj);

            fsObj.Close();

            ms.Close();

            return fileHandle;

        }
        catch (Exception ex)
        {
           return "";
        }

    }        

更新2(针对超时问题)
  • NSURLRequest和NSMutableURLRequest的默认超时时间为60秒,取决于互联网连接、Web服务器上的互联网流量拥塞、图像创建时间等因素,可能不足以完成请求。因此,您可以使用以下语法将超时时间设置得更高:[urlReqObj setTimeoutInterval:180]; 超时间隔仅以秒为单位。

  • 增加IIS中的ConnectionTimeout,默认值为120秒,有时如果请求队列中排队的请求数太多,则会失败。增加ConnectionTimeOut将有所帮助。(此超时时间也适用于连接的空闲时间)。要在IIS高级设置中设置它,请单击IIS Manager中的服务 -> 高级设置 -> 连接限制,然后您可以设置超时时间,或者您可以简单地在web.config中覆盖连接超时时间。

  • 在IIS中打开Keep-alive,以便重复使用相同的连接,从而提高IIS的效率。单击IIS Manager中的服务 -> 高级设置 -> 连接限制 -> 启用HTTP Keep-Alives

  • 如果您计划在数据库中存储图像的二进制数据,则可以增加DB连接字符串中的connectiontimeout或增加sqlcommandtimeout。

希望这有所帮助。 如果您有任何疑问,请随时询问。

嗨,@Duraiamuthan.H。感谢您的回复。但我已经尝试了上述方法,什么也没有发生。您能看到我的服务器端代码吗?也许它是错误的。您能检查一下吗?我也发布了那个。它期望一个字节数组。而不是一个base64编码的字符串。谢谢。 - sreekanthk
@sreekanthk - 欢迎!我已经更新了我的答案!如果你遇到任何问题,请随时问我。 - Durai Amuthan.H
好的,我正在处理中。我会告诉你结果的。谢谢。 - sreekanthk
嗨,我遇到了请求超时的问题。你能帮我解决一下吗? - sreekanthk
这里有另一个与上传图像到服务器和大小问题相关的答案。https://dev59.com/X4jca4cB1Zd3GeqPwm91#29198665 在其他情况下对您有所帮助。 - Durai Amuthan.H

1

你好,

我已经知道了我的问题所在。

服务器端需要一个字节数组作为输入。

因此,我需要按照以下方式发送请求:

将base64字符串转换为字节数组。

postStr = [NSString stringWithFormat:@"<?xml version=\"1.0\"?>\n"
       "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"
       "<s:Body>\n"
       "<InsUpdDelActivityInfo xmlns=\"http://tempuri.org/\">\n"
       "<objEventsContent xmlns:d4p1=\"http://schemas.datacontract.org/2004/07/iCampuslite.Model.ActivityStream\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">\n"
       "<d4p1:ActCommentId i:nil=\"true\" />\n"
       "<d4p1:ActSubTypeCd i:nil=\"true\" />\n"
       "<d4p1:ActType>Status</d4p1:ActType>\n"
       "<d4p1:ActTypeCd>1</d4p1:ActTypeCd>\n"
       "<d4p1:ActivityAnswersList />\n"
       "<d4p1:ActivityComments />\n"
       "<d4p1:ActivityId i:nil=\"true\" />\n"
       "<d4p1:ActivityLike />\n"
       "<d4p1:ActivityName>%@</d4p1:ActivityName>\n"
       "<d4p1:ActivityStreamImagesBytes dt: dt:"Base64Binary"><dt:length>%d</dt:length><dt:byte>%@</dt:byte>......................................</d4p1:ActivityStreamImagesBytes>\n"
       "<d4p1:AnswerDesc i:nil=\"true\" />\n"
       "<d4p1:AnswerId>0</d4p1:AnswerId>\n"
       "<d4p1:CommentDesc i:nil=\"true\" />\n"
       "<d4p1:CommentId i:nil=\"true\" />\n"
       "<d4p1:CreatedUserId>%@</d4p1:CreatedUserId>\n"
       "<d4p1:CreatedUserName i:nil=\"true\" />\n"
       "<d4p1:FileOrLinkName i:nil=\"true\" />\n"
       "<d4p1:IsLiked>0</d4p1:IsLiked>\n"
       "<d4p1:IsTotalSchool i:nil=\"true\" />\n"
       "<d4p1:LikeCount>0</d4p1:LikeCount>\n"
       "<d4p1:LinkImage i:nil=\"true\" />\n"
       "<d4p1:ObjTypeCdId i:nil=\"true\" />\n"
       "<d4p1:ObjTypeId i:nil=\"true\" />\n"
       "<d4p1:OperationMode>I</d4p1:OperationMode>\n"
       "<d4p1:OperationType i:nil=\"true\" />\n"
       "<d4p1:OperationTypeId i:nil=\"true\" />\n"
       "<d4p1:OrganizationId>%@</d4p1:OrganizationId>\n"
       "<d4p1:OtherActivityId i:nil=\"true\" />\n"
       "%@\n"
       "</objEventsContent>\n"
       "<ismobile>true</ismobile>\n"
       "</InsUpdDelActivityInfo>\n"
       "</s:Body>\n"
       "</s:Envelope>", statusText, [byteArray length], byteArray[0], byteArray[1], .................................., [appDelegateObj.loginUserInfoDict valueForKey:@"a:UserId"], [appDelegateObj.loginUserInfoDict valueForKey:@"a:OrgId"], workspaceStr];

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@blah....blah....blah....", appDelegateObj.baseURL]]]; NSString *messageLength = [NSString stringWithFormat:@"%lu", (unsigned long)[postStr length]]; [request addValue:@"text/xml; charset=utf-8" forHTTPHeaderField:@"Content-Type"]; [request addValue:@"http://tempuri.org/blah......blah.....blah............" forHTTPHeaderField:@"SOAPAction"]; [request addValue:messageLength forHTTPHeaderField:@"Content-Length"]; [request setHTTPMethod:@"POST"]; [request setHTTPBody:[postStr dataUsingEncoding:NSUTF8StringEncoding]]; NSError *error = nil; NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&error];
我已经托管了服务并查看了请求的xml格式。无论如何感谢@Duraiamuthan.H

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接