[爲REST形式的WCF使用newtonsoftJson轉換器]之後篇-文件上傳

在爲RESTful的WCF使用了newtonsoftJson轉化器之後,現在需要一個新功能,爲REST WCF增加一個能上傳文件的藉口,如果沒有自定義newtonsoftJson,這個東西只需要寥寥幾筆,就直接實現了文件上傳,但現在自定義了之後,就不行了,因爲自定義的NewtonsoftJsonDispatchFormatter類的DeserializeRequest託管了一切,讓原有的WCF文件上傳功能失去了作用,於是決定自己改造這個類,關於http文件上傳,w3c是有rfc1867規範的,根據這個規範,就有了如下的修改。(此修改只是實現了單文件上傳,多文件上傳還要再次簡單修改一下)

NewtonsoftJsonDispatchFormatter->DeserializeRequest方法

public void DeserializeRequest(Message message, object[] parameters)
        {
            //如果是上傳的content-type,則不作json處理
            var headers = ((HttpRequestMessageProperty)(message.Properties[HttpRequestMessageProperty.Name])).Headers;
            string contenttype = headers["Content-type"];
            string contentLength = headers["Content-Length"];
            if (contenttype.StartsWith("multipart/form-data"))
            {
                //獲得附件分割邊界字符串
                string boundary = contenttype.Substring(contenttype.IndexOf("boundary=") + "boundary=".Length);
                int len = int.Parse(contentLength);
                //if(!(bool)(message.Properties["UriMatched"]))
                //    throw new InvalidOperationException("URL not match.");
                //UriTemplateMatch utr = message.Properties["UriTemplateMatchResults"] as UriTemplateMatch;
                //var d = OperationContext.Current;
                //var m=utr.Template.Match(utr.BaseUri,message.Headers.To);
                
                //獲得方法的參數
                var paramts=operation.SyncMethod.GetParameters();
                int streamtypeIndx = -1;
                int bodyheaderlen = 0;
                //找到Stream類型的參數
                for (streamtypeIndx = 0; streamtypeIndx < paramts.Length && streamtypeIndx < parameters.Length; streamtypeIndx++)
                {
                    if(paramts[streamtypeIndx].ParameterType==typeof(Stream))
                    {
                        //var fl=HttpContext.Current.Request.Files[0];
                        //var stream = fl.InputStream;
                        var stream = message.GetBody<Stream>();
                        //定位到第一個0D0A0D0A)
                        //sb= new StringBuilder(512);
                        int datimes = 0;//回車換行次數
                        int c = 0;
                        while (datimes != 4)
                        {
                            c=stream.ReadByte();
                            if (c == -1)
                                break;
                            if (c == 0x0d && (datimes == 0 || datimes == 2))
                            {
                                datimes++;
                            }
                            else if (c == 0x0a && (datimes == 1 || datimes == 3))
                            {
                                datimes++;
                            }
                            else
                                datimes = 0;
                                
                            //sb.Append((char)c);
                            bodyheaderlen++;
                        }
                        if (c == -1)
                            continue;
                        //計算實際附件大小
                        int fileLength = len - bodyheaderlen - boundary.Length - 6;
                        int remain = fileLength;
                        MemoryStream filestream = new MemoryStream(fileLength);
                        byte[] buffer=new byte[8192];
                        int readed = 0;
                        while (remain>0)
                        {
                            readed = stream.Read(buffer, 0, remain > 8192 ? 8192 : remain);
                            remain -= readed;
                            filestream.Write(buffer, 0, readed);
                        }
                        stream.Close();
                        filestream.Seek(0, SeekOrigin.Begin);
                        //MemoryStream stream = new MemoryStream(message.GetReaderAtBodyContents().ReadElementContentAsBinHex());
                        parameters[streamtypeIndx] = filestream;
                    }
                    else
                    {
                        //string va;
                        //if(headers.t)
                        parameters[streamtypeIndx] = headers[paramts[streamtypeIndx].Name];
                    }
                }
                //if(streamtypeIndx>=parameters.Length)
                //    throw new InvalidOperationException("No System.IO.Stream type parameter in API Method.");

                return;
            }
                
            object bodyFormatProperty;
            if (!message.Properties.TryGetValue(WebBodyFormatMessageProperty.Name, out bodyFormatProperty) ||
                (bodyFormatProperty as WebBodyFormatMessageProperty).Format != WebContentFormat.Raw)
            {
                throw new InvalidOperationException("Incoming messages must have a body format of Raw. Is a ContentTypeMapper set on the WebHttpBinding?");
            }

            XmlDictionaryReader bodyReader = message.GetReaderAtBodyContents();
            bodyReader.ReadStartElement("Binary");
            byte[] rawBody = bodyReader.ReadContentAsBase64();
            MemoryStream ms = new MemoryStream(rawBody);

            StreamReader sr = new StreamReader(ms);
            Newtonsoft.Json.JsonSerializer serializer = new Newtonsoft.Json.JsonSerializer() {
                DateFormatString = "yyyy-MM-dd HH:mm:ss"
            };
            if (parameters.Length == 1)
            {
                // single parameter, assuming bare
                parameters[0] = serializer.Deserialize(sr, operation.Messages[0].Body.Parts[0].Type);
            }
            else
            {
                // multiple parameter, needs to be wrapped
                Newtonsoft.Json.JsonReader reader = new Newtonsoft.Json.JsonTextReader(sr);
                reader.Read();
                if (reader.TokenType != Newtonsoft.Json.JsonToken.StartObject)
                {
                    throw new InvalidOperationException("Input needs to be wrapped in an object");
                }

                reader.Read();
                while (reader.TokenType == Newtonsoft.Json.JsonToken.PropertyName)
                {
                    string parameterName = reader.Value as string;
                    reader.Read();
                    if (this.parameterNames.ContainsKey(parameterName))
                    {
                        int parameterIndex = this.parameterNames[parameterName];
                        parameters[parameterIndex] = serializer.Deserialize(reader, this.operation.Messages[0].Body.Parts[parameterIndex].Type);
                    }
                    else
                    {
                        reader.Skip();
                    }

                    reader.Read();
                }

                reader.Close();
            }

            sr.Close();
            ms.Close();
        }

之後,在自己的Web方法中,只要包含一個Stream參數,參數值就是文件的二進制流

此代碼在Fiddler2上測試通過,再次聲明,現在只支持一個文件上傳


上傳文件的大小限制,可以在配置文件中進行修改

      <webHttpBinding>
        <binding name="test1" maxReceivedMessageSize="6291456" maxBufferSize="6291456" contentTypeMapper="xxx.xxx.xxx,xxx">
          

這裏的maxReceivedMessageSize大體上等於文件的大小
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章