文章目录▼CloseOpen
- 先把WebService接口搭对——这是上传成功的基础
- 第一步:新建WebService项目(以Visual Studio为例)
- 第二步:写接收照片的核心方法
- 第三步:配置WebService的关键参数
- 附:WebService接口常见参数说明表
- Flex端怎么写?——关键是处理二进制流和状态回调
- 第一步:MXML里的UI组件(用来让用户操作)
- 第二步:Flex端的关键注意事项(我踩过的坑)
- 避坑指南:我踩过的5个坑,你别再踩
- WebService接收照片的方法里,fileData参数为什么要用byte[]类型?
- Flex里选完文件直接点上传,为什么传过去的是空数据?
- Flex和WebService不在一个域名下,上传时提示跨域错误怎么办?
- WebService接口报“拒绝访问”,是不是服务器文件夹的问题?
- Flex调用WebService时提示“方法未找到”,是哪里没配置对?
这篇文章就是为解决这个痛点来的——我们把Flex端的上传逻辑、WebService接口的配置方法,甚至二进制流处理、状态回调、错误捕获这些容易踩坑的细节,都拆成了清晰的 step-by-step 步骤,还附了能直接复制运行的完整代码示例。不管你是刚接触Flex与WebService整合,还是之前踩过“传图失败”的坑,跟着文章走,就能快速搞定从接口搭建到Flex端调用的全流程,不用再对着零散资料瞎琢磨。 咱们直接上能落地的干货。
你有没有过这种情况?维护老Flex项目时,要做照片上传功能,搜遍网上的代码要么缺WebService接口配置细节,要么Flex端二进制处理没讲透——调试时要么传上去的是损坏文件,要么接口直接报错,折腾好几天都没搞定?我去年帮朋友调他们公司的老Flex系统时,就踩过一模一样的坑:当时找了三四个教程,要么步骤跳得太快(比如直接甩个WebService方法却没说怎么配置ContentType),要么代码片段少关键参数(比如没提FileReference要等load完成才能拿数据),最后还是自己一点点试出来的。今天就把我整理的全套方法分享给你——从WebService接口搭建到Flex端调用,每一步都有能直接复制的代码,还有我踩过的坑的避坑指南,亲测跟着做就能成。
先把WebService接口搭对——这是上传成功的基础
WebService是上传的“接收端”,接口没搭对,后面Flex再怎么调都是白搭。老项目常用的WebService技术无非两种:.NET的ASMX(就是后缀为.asmx的文件)或者Java的JAX-WS(比如用Axis2搭建)。我当时帮朋友用的是.NET的ASMX,因为他们的老系统是.NET做的,兼容性更好,这里就以它为例讲——Java的逻辑其实差不多,换汤不换药。
第一步:新建WebService项目(以Visual Studio为例)
打开Visual Studio,新建“ASP.NET Web服务应用程序”(注意选.NET Framework版本,老项目通常用4.0或更低)。新建后会自动生成一个Service1.asmx文件,这就是你的WebService接口文件。
第二步:写接收照片的核心方法
接下来要写一个接收二进制文件的WebMethod——重点是两个参数:fileName
(保存的文件名,要带扩展名)和fileData
(照片的二进制流,必须用byte[]
类型)。直接上我当时写的代码:
using System;
using System.Web;
using System.Web.Services;
using System.IO;
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.Web.Script.Services.ScriptService] // 允许AJAX调用(Flex调用也需要)
public class PhotoUploadService System.Web.Services.WebService
{
[WebMethod] // 标记为WebService方法
public string UploadPhoto(string fileName, byte[] fileData)
{
try
{
//
获取服务器上的物理保存路径(必须用MapPath转换,否则会找不到文件夹)
string uploadFolder = HttpContext.Current.Server.MapPath("~/Uploads/");
//
确保Uploads文件夹存在(如果不存在就创建)
if (!Directory.Exists(uploadFolder))
{
Directory.CreateDirectory(uploadFolder);
}
//
拼接完整保存路径(fileName要带扩展名,比如"avatar.jpg")
string savePath = Path.Combine(uploadFolder, fileName);
//
把二进制流写入文件
File.WriteAllBytes(savePath, fileData);
//
返回成功信息(可以根据需要返回保存路径或其他标识)
return $"上传成功:文件保存至{savePath}";
}
catch (Exception ex)
{
// 捕获异常,返回错误信息(方便Flex端调试)
return $"上传失败:{ex.Message}";
}
}
}
这段代码里有几个我踩过的坑,必须重点提醒你:
ScriptService
特性——一开始我没加这个,结果Flex调用时提示“无法访问该方法”,后来查MSDN才知道,这个特性是允许非浏览器客户端(比如Flex)调用WebService的关键; Directory.CreateDirectory
才解决; string savePath = "C:\Uploads\" + fileName
,结果部署到服务器时,因为服务器的C盘没有写权限,直接报错,换成MapPath
才对(它会把虚拟路径~/Uploads/
转换成服务器上的物理路径,比如D:WebsiteUploads
)。第三步:配置WebService的关键参数
写好方法还不够,得在Web.config里加两行配置——允许WebService接收POST请求(因为Flex传二进制流要用POST):
<!-
允许POST请求,必须加! >
一定要给Uploads文件夹加写入权限!我当时就是忘了这步,结果报“拒绝访问”错误——解决方法是:找到服务器上的Uploads文件夹→右键“属性”→“安全”→添加“IIS_USRS”或“NETWORK SERVICE”用户,勾选“写入”权限。
附:WebService接口常见参数说明表
为了让你更清楚每个参数的作用,我整理了一张表——这是我当时调试时记的笔记,直接搬给你:
参数名 | 类型 | 作用 | 避坑提示 |
---|---|---|---|
fileName | string | 保存的文件名(含扩展名) | 必须带扩展名(如”avatar.jpg”),否则无法识别文件类型;避免特殊字符(如/、、:),可以用Path.GetFileName(fileName) 过滤 |
fileData | byte[] | 照片的二进制流 | 绝对不能用string类型!否则二进制流会被转换成文本,导致文件损坏 |
savePath | string | 服务器保存路径 | 必须用MapPath 转换虚拟路径,避免直接写绝对路径(如”C:Uploads”) |
Flex端怎么写?——关键是处理二进制流和状态回调
WebService接口搭好后,接下来是Flex端的“发送端”。Flex里处理文件上传的核心类是FileReference
(用来选择和读取文件)和WebService
(用来调用WebService接口)。我当时写的Flex代码,现在还存在朋友的项目里——直接复制就能用,我帮你标了关键注释。
第一步:MXML里的UI组件(用来让用户操作)
先在MXML文件里加几个组件:选择文件的按钮、上传按钮、进度条、状态提示——这些是用户能看到的交互元素:
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="600" minHeight="400"
initialize="init()"> <!-
初始化方法,页面加载时执行 >
<![CDATA[
// 导入需要的类(不用自己写,Flex会自动提示)
import flash.net.FileReference;
import flash.events.Event;
import flash.events.ProgressEvent;
import mx.rpc.soap.WebService;
import mx.rpc.events.ResultEvent;
import mx.rpc.events.FaultEvent;
// 声明变量:FileReference用来选择文件,WebService用来调用接口
private var fileRef:FileReference = new FileReference();
private var ws:WebService = new WebService();
// 标记是否已选择文件(控制上传按钮是否可用)
private var isFileSelected:Boolean = false;
// 标记是否正在上传(控制进度条是否显示)
private var isUploading:Boolean = false;
// 初始化方法:页面加载时配置WebService和FileReference
private function init():void {
//
配置WebService的WSDL地址(就是你WebService的asmx文件地址,比如http://localhost:8080/PhotoUploadService.asmx?WSDL)
ws.wsdl = "http://你的服务器地址/PhotoUploadService.asmx?WSDL";
//
监听WebService的结果事件(上传成功时触发)
ws.addEventListener(ResultEvent.RESULT, onUploadResult);
//
监听WebService的错误事件(上传失败时触发)
ws.addEventListener(FaultEvent.FAULT, onUploadFault);
//
配置FileReference:监听文件选择事件(用户选完文件后触发)
fileRef.addEventListener(Event.SELECT, onFileSelected);
//
配置FileReference:监听文件读取完成事件(必须等这个事件触发,才能拿到文件的二进制流)
fileRef.addEventListener(Event.COMPLETE, onFileLoadComplete);
//
配置FileReference:监听上传进度事件(用来更新进度条)
fileRef.addEventListener(ProgressEvent.PROGRESS, onUploadProgress);
}
// 选择文件的按钮点击事件:打开文件选择对话框
private function selectFile():void {
// 过滤文件类型:只允许选图片(jpg、png、gif)
var imageFilter:FileFilter = new FileFilter("图片文件", ".jpg;.png;.gif");
fileRef.browse([imageFilter]); // 弹出文件选择框
}
// 文件选择完成事件:用户选完文件后触发
private function onFileSelected(event:Event):void {
isFileSelected = true; // 标记已选择文件,让上传按钮可用
statusLabel.text = "已选择文件:" + fileRef.name; // 显示选中的文件名
fileRef.load(); // 开始读取文件(必须调用load(),才能拿到二进制流)
}
// 文件读取完成事件:FileReference加载完文件后触发(此时才能拿到fileRef.data)
private function onFileLoadComplete(event:Event):void {
statusLabel.text = "文件读取完成, ready to upload!";
}
// 上传按钮点击事件:调用WebService接口上传文件
private function uploadFile():void {
if (!isFileSelected) {
statusLabel.text = "请先选择文件!";
return;
}
isUploading = true; // 标记正在上传,显示进度条
statusLabel.text = "正在上传...";
// 关键步骤:调用WebService的UploadPhoto方法,传递参数
// 第一个参数是WebService方法名,第二个参数是参数对象(key要和WebService方法的参数名一致!)
ws.call("UploadPhoto", { fileName: fileRef.name, fileData: fileRef.data });
}
// 上传进度事件:更新进度条
private function onUploadProgress(event:ProgressEvent):void {
// 计算上传进度百分比(已上传字节数/总字节数100)
var progressPercent:Number = Math.round((event.bytesLoaded / event.bytesTotal) 100);
progressBar.label = progressPercent + "%"; // 显示百分比
progressBar.setProgress(event.bytesLoaded, event.bytesTotal); // 设置进度条
}
// 上传成功事件:WebService返回结果时触发
private function onUploadResult(event:ResultEvent):void {
isUploading = false; // 标记上传完成,隐藏进度条
statusLabel.text = event.result.toString(); // 显示WebService返回的信息(比如"上传成功:...")
}
// 上传失败事件:WebService报错时触发
private function onUploadFault(event:FaultEvent):void {
isUploading = false; // 标记上传完成,隐藏进度条
statusLabel.text = "上传失败:" + event.fault.faultString; // 显示错误信息(方便调试)
}
]]>
<!-
UI组件:按顺序排列 >
<!-
只有选了文件,按钮才可用 >
<!-
正在上传时显示进度条 >
<!-
显示状态信息 >
第二步:Flex端的关键注意事项(我踩过的坑)
这段代码里有几个绝对不能忽略的点——都是我当时掉过的坑,帮你提前避了:
fileRef.load()
:我一开始以为选完文件就能直接拿fileRef.data
,结果传过去的是空值——后来才知道,FileReference
的data
属性只有在load()
完成后(也就是COMPLETE
事件触发后)才有值; fileName
和fileData
,Flex里调用时的参数对象key必须一模一样({ fileName: fileRef.name, fileData: fileRef.data }
),否则WebService收不到参数; http://localhost:3000
,WebService在http://localhost:8080
),必须在WebService的根目录放一个crossdomain.xml
文件,允许Flex跨域访问。crossdomain.xml
的内容我帮你写好了,直接复制: xml
<allow-access-from domain="“> <!-
<allow-http-request-headers-from domain="” headers=”“> <!-
监听,用户上传大文件时看不到进度,以为卡了——后来加上进度条,用户体验好了不止一点。
避坑指南:我踩过的5个坑,你别再踩
最后再帮你整理一下我当时踩过的“血泪坑”,都是调试时最容易犯的错,记下来能少走很多弯路:
特性
:导致Flex无法调用接口,报错“方法未找到”——解决方法:在WebService类上加上[System.Web.Script.Services.ScriptService];
事件就调用接口
:导致fileRef.data为空,传过去的是0字节文件——解决方法:必须在
onFileLoadComplete事件里处理上传; 4.
WebService接收照片的方法里,fileData参数为什么要用byte[]类型?
因为照片是二进制数据,如果用string类型,二进制流会被转换成文本格式,传过去的文件会损坏打不开。文章里也提到这是常见坑,只有byte[]才能保证数据完整。
Flex里选完文件直接点上传,为什么传过去的是空数据?
因为FileReference的data属性不是选完就有,得等它load完成——也就是COMPLETE事件触发后,才能拿到真正的二进制流。我之前帮朋友调试时就犯过这错,后来加了fileRef.load()和COMPLETE事件监听才解决。
Flex和WebService不在一个域名下,上传时提示跨域错误怎么办?
要在WebService的根目录放一个crossdomain.xml文件,允许Flex跨域访问。文件里要写允许的域名(测试时可用*)和请求头,文章里有现成的内容,直接复制用就行。
WebService接口报“拒绝访问”,是不是服务器文件夹的问题?
大概率是Uploads文件夹没有写入权限。找到这个文件夹,右键属性加权限——给IIS_USRS或NETWORK SERVICE用户勾选“写入”,这样WebService才能把照片存进去。
Flex调用WebService时提示“方法未找到”,是哪里没配置对?
首先检查WebService类上有没有加ScriptService特性,这是Flex能调用的关键。另外要确认Flex里的参数名和WebService方法的参数名一模一样,比如WebService是fileName,Flex就不能写成file_name,否则接口收不到参数。