在Silverlight上实现文件上传的例子在网上的还不多,特别是多文件上传和大文件上传的例子就更少了。当然
那些商品软件公司的产品除外。

目前的CodePlex上就有这样一个项目,其链接:http://www.codeplex.com/SLFileUpload/ ,他的个人主
站链接:http://www.michielpost.nl/    
    
     我在本地下载运行其代码后,发现“果然”很好用,而且代码写的也很规范。当然其也是免费的,但作者并不
拒绝各种名义上的“捐助(Donate)”。

下面就是其“汉化”后的运行截图,首先是多文件上传
    
         

然后是大文件上传:
    
         

根据作者的README文件,其支持下面几个初始化参数:

    MaxFileSizeKB:  File size in KBs.
    MaxUploads:  Maximum number of simultaneous uploads
    FileFilter: File filter, for example ony jpeg use: FileFilter=Jpeg (*.jpg) |*.jpg
    CustomParam: Your custom parameter, anything here will be available in the WCF webservice
    DefaultColor: The default color for the control, for example: LightBlue

当然,里面的服务端采用WCF方法。为了考虑在.net1框架上也可以使用,我在保留原有代码结构的基础上,将WCF
用ASMX格式拷贝了一份,经过编译,完成可以运行:)

同时为了便于大家阅读源码,我还加入了中文说明(源码中注释很少,而且是EN文)。下面就是其主要的几个类的
定义和说明:

FileCollection 上传文件集合类,用于UI统一访问和操作:

Code
/// <summary>
/// 文件集合管理类
/// 注:ObservableCollection是个泛型集合类,往其中添加或去除条目时(或者其中的条目实现了INotifyPropertyChanged的话,在属性变动时),
/// 它会发出变化通知事件(先执行集合类中的同名属性)。这在做数据绑定时会非常方便,因为UI控件可以使用这些通知来知道自动刷新它们的值,
/// 而不用开发人员编写代码来显式地这么做。
/// </summary>
public class FileCollection : ObservableCollection<UserFile>
{
    /// <summary>
    /// 已上传的累计(多文件)字节数
    /// </summary>
    private double _bytesUploaded = 0;
    /// <summary>
    /// 已上传字符数占全部字节数的百分比
    /// </summary>
    private int _percentage = 0;
    /// <summary>
    /// 当前正在上传的文件序号
    /// </summary>
    private int _currentUpload = 0;
    /// <summary>
    /// 上传初始化参数,详情如下:
    /// MaxFileSizeKB:  File size in KBs.
    /// MaxUploads:  Maximum number of simultaneous uploads
    /// FileFilter: File filter, for example ony jpeg use: FileFilter=Jpeg (*.jpg) |*.jpg
    /// CustomParam: Your custom parameter, anything here will be available in the WCF webservice
    /// DefaultColor: The default color for the control, for example: LightBlue
    /// </summary>
    private string _customParams;
    /// <summary>
    /// 最大上传字节数
    /// </summary>
    private int _maxUpload;
    
    /// <summary>
    /// 已上传的累计(多文件)字节数,该字段的修改事件通知会发给page.xmal中的TotalKB
    /// </summary>
    public double BytesUploaded
    {
        get { return _bytesUploaded; }
        set
        {
            _bytesUploaded = value;
            this.OnPropertyChanged(new PropertyChangedEventArgs("BytesUploaded"));
        }
    }

/// <summary>
    /// 已上传字符数占全部字节数的百分比,该字段的修改事件通知会发给page.xmal中的TotalProgress
    /// </summary>
    public int Percentage
    {
        get { return _percentage; }
        set
        {
            _percentage = value;
            this.OnPropertyChanged(new PropertyChangedEventArgs("Percentage"));
        }
    }

/// <summary>
    /// 构造方法
    /// </summary>
    /// <param name="customParams"></param>
    /// <param name="maxUploads"></param>
    public FileCollection(string customParams, int maxUploads)
    {
        _customParams = customParams;
        _maxUpload = maxUploads;

this.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(FileCollection_CollectionChanged);
    }

/// <summary>
    /// 依次加入所选的上传文件信息
    /// </summary>
    /// <param name="item"></param>
    public new void Add(UserFile item)
    {
        item.PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);            
        base.Add(item);
    }

/// <summary>
    /// 单个上传文件属性改变时
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        //当属性变化为“从上传列表中移除”
        if (e.PropertyName == "IsDeleted")
        {
            UserFile file = (UserFile)sender;

if (file.IsDeleted)
            {
                if (file.State == Constants.FileStates.Uploading)
                {
                    _currentUpload--;
                    UploadFiles();
                }

this.Remove(file);

file = null;
            }
        }
        //当属性变化为“开始上传”
        else if (e.PropertyName == "State")
        {
            UserFile file = (UserFile)sender;
            //此时file.State状态为ploading
            if (file.State == Constants.FileStates.Finished || file.State == Constants.FileStates.Error)
            {
                _currentUpload--;
                UploadFiles();
            }
        }
        //当属性变化为“上传进行中”
        else if (e.PropertyName == "BytesUploaded")
        {
            //重新计算上传数据
            RecountTotal();
        }
    }

/// <summary>
    /// 上传文件
    /// </summary>
    public void UploadFiles()
    {
        lock (this)
        {
            foreach (UserFile file in this)
            {   //当上传文件未被移除(IsDeleted)或是暂停时
                if (!file.IsDeleted && file.State == Constants.FileStates.Pending && _currentUpload < _maxUpload)
                {
                    file.Upload(_customParams);
                    _currentUpload++;
                }
            }
        }

}

/// <summary>
    /// 重新计算数据
    /// </summary>
    private void RecountTotal()
    {
        //Recount total
        double totalSize = 0;
        double totalSizeDone = 0;

foreach (UserFile file in this)
        {
            totalSize += file.FileSize;
            totalSizeDone += file.BytesUploaded;
        }

double percentage = 0;

if (totalSize > 0)
            percentage = 100 * totalSizeDone / totalSize;

BytesUploaded = totalSizeDone;

Percentage = (int)percentage;
    }

/// <summary>
    /// 当添加或取消上传文件时触发
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    void FileCollection_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        //当集合信息变化时(添加或删除项)时,则重新计算数据 
        RecountTotal();
    }
}

上传文件信息类:

Code
/// <summary>
/// 上传文件信息类
/// </summary>
public class UserFile : INotifyPropertyChanged
{
    /// <summary>
    /// 上传文件名称
    /// </summary>
    private string _fileName;
    /// <summary>
    /// 是否取消上传该文件
    /// </summary>
    private bool _isDeleted = false;      
    /// <summary>
    /// 上传文件的流信息
    /// </summary>
    private Stream _fileStream;
    /// <summary>
    /// 当前上传文件状态
    /// </summary>
    private Constants.FileStates _state = Constants.FileStates.Pending;
    /// <summary>
    /// 当前已上传的字节数(这里与FileCollection中的同名属性意义不同,FileCollection中的是已上传的所有文件的字节总数)
    /// </summary>
    private double _bytesUploaded = 0;
    /// <summary>
    /// 当前文件大小
    /// </summary>
    private double _fileSize = 0;
    /// <summary>
    /// 已上传文件的百分比
    /// </summary>
    private int _percentage = 0;
    /// <summary>
    /// 上传文件操作类
    /// </summary>
    private FileUploader _fileUploader;

/// <summary>
    /// 上传文件名称
    /// </summary>
    public string FileName
    {
        get { return _fileName; }
        set
        {
            _fileName = value;
            NotifyPropertyChanged("FileName");
        }
    }

/// <summary>
    /// 当前上传文件的状态,注意这时使用了NotifyPropertyChanged来通知FileRowControl控件中的FileRowControl_PropertyChanged事件
    /// </summary>
    public Constants.FileStates State
    {
        get { return _state; }
        set
        {
            _state = value;

NotifyPropertyChanged("State");
        }
    }

/// <summary>
    /// 当前上传文件是否已被移除,注意这时使用了NotifyPropertyChanged来通知FileCollection类中的item_PropertyChanged事件
    /// </summary>
    public bool IsDeleted
    {
        get { return _isDeleted; }
        set
        {
            _isDeleted = value;

if (_isDeleted)
                CancelUpload();

NotifyPropertyChanged("IsDeleted");
        }
    }

/// <summary>
    /// 上传文件的流信息
    /// </summary>
    public Stream FileStream
    {
        get { return _fileStream; }
        set
        {
            _fileStream = value;

if (_fileStream != null)
                _fileSize = _fileStream.Length;                
            
        }
    }

/// <summary>
    /// 当前文件大小
    /// </summary>
    public double FileSize
    {
        get {
            return _fileSize;               
        }
    }

/// <summary>
    /// 当前已上传的字节数(这里与FileCollection中的同名属性意义不同,FileCollection中的是已上传的所有文件的字节总数)
    /// </summary>
    public double BytesUploaded
    {
        get { return _bytesUploaded; }
        set
        {
            _bytesUploaded = value;

NotifyPropertyChanged("BytesUploaded");

Percentage = (int)((value * 100) / _fileStream.Length);

}
    }

/// <summary>
    /// 已上传文件的百分比(这里与FileCollection中的同名属性意义不同,FileCollection中的是已上传字符数占全部字节数的百分比,该字段的修改事件通知会发给page.xmal中的TotalProgress)
    /// </summary>
    public int Percentage
    {
        get { return _percentage; }
        set
        {
            _percentage = value;
            NotifyPropertyChanged("Percentage");
        }
    }

/// <summary>
    /// 上传当前文件
    /// </summary>
    /// <param name="initParams"></param>
    public void Upload(string initParams)
    {
        this.State = Constants.FileStates.Uploading;

_fileUploader = new FileUploader(this);            
        _fileUploader.UploadAdvanced(initParams);
        _fileUploader.UploadFinished += new EventHandler(fileUploader_UploadFinished);            
    }

/// <summary>
    /// 取消上传,注:该文件仅在本类中的IsDeleted属性中使用
    /// </summary>
    public void CancelUpload()
    {
        if (_fileUploader != null && this.State == Constants.FileStates.Uploading)
        {
            _fileUploader.CancelUpload();
        }
    }

/// <summary>
    /// 当前文件上传完成时
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    void fileUploader_UploadFinished(object sender, EventArgs e)
    {
        _fileUploader = null;

this.State = Constants.FileStates.Finished;
    }

#region INotifyPropertyChanged Members

private void NotifyPropertyChanged(string prop)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }
    }

public event PropertyChangedEventHandler PropertyChanged;

#endregion
}

上传文件操作类(实现文件上传功能代码):

Code
/// <summary>
/// 文件上传类
/// </summary>
public class FileUploader
{
    private UserFile _file;
    private long _dataLength;
    private long _dataSent;
    private SilverlightUploadServiceSoapClient _client;
    private string _initParams;
    private bool _firstChunk = true;
    private bool _lastChunk = false;

public FileUploader(UserFile file)
    {
        _file = file;

_dataLength = _file.FileStream.Length;
        _dataSent = 0;

//创建WCF端,此处被注释
        //BasicHttpBinding binding = new BasicHttpBinding();
        //EndpointAddress address = new EndpointAddress(new CustomUri("SilverlightUploadService.svc"));
        //_client = new UploadService.UploadServiceClient(binding, address);
        //_client = new UploadService.UploadServiceClient();
        //_client.InnerChannel.Closed += new EventHandler(InnerChannel_Closed);
        
        //创建webservice客户端
        _client = new SilverlightUploadServiceSoapClient();
        //事件绑定
        _client.StoreFileAdvancedCompleted += new EventHandler<System.ComponentModel.AsyncCompletedEventArgs>(_client_StoreFileAdvancedCompleted);
        _client.CancelUploadCompleted += new EventHandler<System.ComponentModel.AsyncCompletedEventArgs>(_client_CancelUploadCompleted);
        _client.ChannelFactory.Closed += new EventHandler(ChannelFactory_Closed);
    }

#region
    /// <summary>
    /// 关闭ChannelFactory事件
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    void ChannelFactory_Closed(object sender, EventArgs e)
    {
        ChannelIsClosed();
    }

void _client_CancelUploadCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
    {
        //当取消上传完成后关闭Channel
        _client.ChannelFactory.Close();
    }

/// <summary>
    /// Channel被关闭
    /// </summary>
    private void ChannelIsClosed()
    {
        if (!_file.IsDeleted)
        {
            if (UploadFinished != null)
                UploadFinished(this, null);
        }
    }

/// <summary>
    /// 取消上传
    /// </summary>
    public void CancelUpload()
    {
        _client.CancelUploadAsync(_file.FileName);
    }
    #endregion

/// <summary>
    /// 上传完成事件处理对象声明
    /// </summary>
    public event EventHandler UploadFinished;

public void UploadAdvanced(string initParams)
    {
        _initParams = initParams;

UploadAdvanced();
    }

/// <summary>
    /// 上传文件
    /// </summary>
    private void UploadAdvanced()
    {
        
        byte[] buffer = new byte[4 * 4096];
        int bytesRead = _file.FileStream.Read(buffer, 0, buffer.Length);

//文件是否上传完毕?
        if (bytesRead != 0)
        {
            _dataSent += bytesRead;

if (_dataSent == _dataLength)
                _lastChunk = true;//是否是最后一块数据,这样WCF会在服务端根据该信息来决定是否对临时文件重命名

//上传当前数据块
            _client.StoreFileAdvancedAsync(_file.FileName, buffer, bytesRead, _initParams, _firstChunk, _lastChunk);

//在第一条消息之后一直为false
            _firstChunk = false;

//通知上传进度修改
            OnProgressChanged();
        }
        else
        {
            //当上传完毕后
            _file.FileStream.Dispose();
            _file.FileStream.Close();

_client.ChannelFactory.Close();          
        }

}

/// <summary>
    /// 修改进度属性
    /// </summary>
    private void OnProgressChanged()
    {
        _file.BytesUploaded = _dataSent;//注:此处会先调用FileCollection中的同名属性,然后才是_file.BytesUploaded属性绑定
    }

void _client_StoreFileAdvancedCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
    {
        //检查WEB服务是否存在错误
        if (e.Error != null)
        {
            //当错误时放弃上传
            _file.State = Constants.FileStates.Error;
        }
        else
        {
            //如果文件未取消上传的话,则继续上传
            if (!_file.IsDeleted)
                UploadAdvanced();
        }
    }

}

服务端WCF代码如下(ASMX文件代码与其基本相同):

Code
[AspNetCompatibilityRequirements  (RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class UploadService : IUploadService
{
    private string _tempExtension = "_temp";

#region IUploadService Members

/// <summary>
    /// 取消上传
    /// </summary>
    /// <param name="fileName"></param>
    public void CancelUpload(string fileName)
    {
        string uploadFolder = GetUploadFolder();
        string tempFileName = fileName + _tempExtension;

if (File.Exists(@HostingEnvironment.ApplicationPhysicalPath + "/" + uploadFolder + "/" + tempFileName))
            File.Delete(@HostingEnvironment.ApplicationPhysicalPath + "/" + uploadFolder + "/" + tempFileName);
    }

public void StoreFileAdvanced(string fileName, byte[] data, int dataLength, string parameters, bool firstChunk, bool lastChunk)
    {
        string uploadFolder = GetUploadFolder();
        string tempFileName = fileName + _tempExtension;

//当上传文件的第一批数据时,先清空以往的相同文件名的文件(同名文件可能为上传失败造成)
        if (firstChunk)
        {
            //删除临时文件
            if (File.Exists(@HostingEnvironment.ApplicationPhysicalPath + "/" + uploadFolder + "/" + tempFileName))
                File.Delete(@HostingEnvironment.ApplicationPhysicalPath + "/" + uploadFolder + "/" + tempFileName);

//删除目标文件
            if (File.Exists(@HostingEnvironment.ApplicationPhysicalPath + "/" + uploadFolder + "/" + fileName))
                File.Delete(@HostingEnvironment.ApplicationPhysicalPath + "/" + uploadFolder + "/" + fileName);

}

FileStream fs = File.Open(@HostingEnvironment.ApplicationPhysicalPath + "/" + uploadFolder + "/" + tempFileName, FileMode.Append);
        fs.Write(data, 0, dataLength);
        fs.Close();

if (lastChunk)
        {
            //将临时文件重命名为原来的文件名称
            File.Move(HostingEnvironment.ApplicationPhysicalPath + "/" + uploadFolder + "/" + tempFileName, HostingEnvironment.ApplicationPhysicalPath + "/" + uploadFolder + "/" + fileName);

//Finish stuff.
            FinishedFileUpload(fileName, parameters);
        }

}

/// <summary>
    /// 删除上传文件
    /// </summary>
    /// <param name="fileName"></param>
    protected void DeleteUploadedFile(string fileName)
    {
        string uploadFolder = GetUploadFolder();

if (File.Exists(@HostingEnvironment.ApplicationPhysicalPath + "/" + uploadFolder + "/" + fileName))
            File.Delete(@HostingEnvironment.ApplicationPhysicalPath + "/" + uploadFolder + "/" + fileName);
    }

protected virtual void FinishedFileUpload(string fileName, string parameters)
    {
    }

/// <summary>
    /// 获取上传路径
    /// </summary>
    /// <returns></returns>
    protected virtual string GetUploadFolder()
    {
        return "Upload";
    }     
    #endregion
}

当然在该DEMO中,其支持两种初始化方式,一种是:

<asp:Silverlight ID="Xaml1" runat="server" Source="~/ClientBin/mpost.SilverlightMultiFileUpload.xap" MinimumVersion="2.0.30523"  Width="415" Height="280"   InitParameters="MaxFileSizeKB=1000,MaxUploads=2,FileFilter=,CustomParam=1,DefaultColor=LightBlue"  />

另一种是在ServiceReferences.ClientConfig中进行文件配置:

<appSettings>
       <add key="MaxFileSizeKB" value="50" />
       <add key="FileFilter" value="Photo's (*.jpg)|*.jpg" />
       <add key="FileFilter" value="" />
       <add key="MaxUploads" value="2" />
</appSettings>

而加载顺序要是自上而下,代码段如下(摘自Page.xaml.cs):

Code
/// <summary>
/// 加载配置参数 then from .Config file
/// </summary>
/// <param name="initParams"></param>
private void LoadConfiguration(IDictionary<string, string> initParams)
{
    string tryTest = string.Empty;

//加载定制配置信息串
    if (initParams.ContainsKey("CustomParam") && !string.IsNullOrEmpty(initParams["CustomParam"]))
        _customParams = initParams["CustomParam"];

if (initParams.ContainsKey("MaxUploads") && !string.IsNullOrEmpty(initParams["MaxUploads"]))
    {
        int.TryParse(initParams["MaxUploads"], out _maxUpload);            
    }

if (initParams.ContainsKey("MaxFileSizeKB") && !string.IsNullOrEmpty(initParams["MaxFileSizeKB"]))
    {
        if (int.TryParse(initParams["MaxFileSizeKB"], out _maxFileSize))
            _maxFileSize = _maxFileSize * 1024;
    }

if (initParams.ContainsKey("FileFilter") && !string.IsNullOrEmpty(initParams["FileFilter"]))
        _fileFilter = initParams["FileFilter"];

//从配置文件中获取相关信息
    if (!string.IsNullOrEmpty(ConfigurationManager.AppSettings["MaxFileSizeKB"]))
    {
        if (int.TryParse(ConfigurationManager.AppSettings["MaxFileSizeKB"], out _maxFileSize))
            _maxFileSize = _maxFileSize * 1024;
    }

if(!string.IsNullOrEmpty(ConfigurationManager.AppSettings["MaxUploads"]))
        int.TryParse(ConfigurationManager.AppSettings["MaxUploads"], out _maxUpload);

if(!string.IsNullOrEmpty( ConfigurationManager.AppSettings["FileFilter"]))
        _fileFilter = ConfigurationManager.AppSettings["FileFilter"];

}

好了,今天的内容就先到这里了,感兴趣的朋友可以在回复中进行讨论或给他(作者)留言,contact@MichielPost.nl

作者:代震军,daizhj
   
    tags:silverlight,uploade, 文件上传, 多文件,大文件
    
    中文注释的源码下载,请点击这里。
   
    CodePlex, 下载链接:)

推荐一个Silverlight多文件(大文件)上传的开源项目(转载)相关推荐

  1. Silverlight多文件(大文件)上传的开源项目

    在Silverlight上实现文件上传的例子在网上的还不多,特别是多文件上传和大文件上传的例子就更少了.当然 那些商品软件公司的产品除外. 目前的CodePlex上就有这样一个项目,其链接:http: ...

  2. 如何在 GitHub 上高效搜索开源项目(转载)

    如何在 GitHub 上高效搜索开源项目 查看全文 http://www.taodudu.cc/news/show-1347360.html 相关文章: Mybatis.cfg配置标签的顺序问题 To ...

  3. java 开源 断点续传,全平台大文件断点续传上传技术 ( 开源项目 Stream )

    Stream 上传插件介绍 Stream 这个项目主要是为了解决大文件上传, 本程序只是它的一个 Perl 后端的实现. 项目网站是: http://www.twinkling.cn 原始地址是: h ...

  4. 基于新版本Gradle上传jitpack开源项目

    因为新版本的gradle编译开源库的方法已经变了,所以这边记录下 1.第一步创建一个空项目 2.因为这样会创建出来一个app项目,我们上传git是不需要这个的,所以我们在 项目中setting.gra ...

  5. Asp.Net Core文件或图片上传

    管理后台上传的图片或文件可能也需要在 Api 项目能够访问,考虑到文件的统一管理,后台的文件默认 是上传到 Api 项目里的. Web 项目里的配置文件 appsetting.json 中的 ApiS ...

  6. php webuploader大文件,web uploader 上传大文件总结

    由于业务需要,需要上传大文件,已有的版本无法处理IE版本,经过调研,百度的 webuploader 支持 IE 浏览器,而且支持计算MD5值,进而可以实现秒传的功能. 大文件上传主要分为三部分,预上传 ...

  7. form表单刷新_《大胖 ? 小课》- 不用 js 实现文件无刷新上传

    这是<大胖小课>栏目的专题一<说说文件上传那些事儿>的第3节-<不用 js 实现文件无刷新上传> 专题已经更新章节: <大胖 • 小课>- 我是这样理解 ...

  8. formdata上传文件_大文件分片断点上传实现思路以及方案

    作者:yeyan1996| 来源:掘金https://juejin.im/post/5dff8a26e51d4558105420ed 前言 我在面试的时候确实被问到了这个问题,而且是一道在线 codi ...

  9. vue+element-ui大文件的分片上传和断点续传js-spark-md5和browser-md5-file

    注意:以下共两份代码片段,第一份为原博主链接代码,第二份自己写的整体代码(比较乱) 1.参考 https://www.cnblogs.com/kelelipeng/p/10158599.html (j ...

最新文章

  1. 你需要学好知识图谱——用AI技术连接世界
  2. 关闭word_Word教程第2讲:文档的基本操作(含视频)
  3. C#类、接口、虚方法和抽象方法
  4. 用matlab实现假设性检验T检验(葡萄酒评价)
  5. js获取服务器响应头信息,请问,js中请求头信息和返回头信息的方法
  6. 吐血整理!内部包含3980大数据、机器学习、推荐系统实战课程,仅分享一次
  7. Linux下安装ntp时间同步服务器
  8. 1. 变量和基本类型
  9. Log4j2的常用配置
  10. Java 对字符以及字符数组的操作
  11. 大数据行业人士必知10大数据思维原理
  12. PHP教程一环境搭建 Wampserve 下载和安装
  13. 高性能RPC框架BRPC核心机制分析
  14. 接口测试, 功能测试(黑盒测试)
  15. tan(x)的四阶麦克劳林级数
  16. Python升级之路( Lv9 ) 文件操作
  17. 东莞市重点培育上市后备科技企业名单(科技局)
  18. VR直播面临的五大技术挑战和实现难点
  19. 大家是怎么学好python啊??
  20. 最新 跟我学spring3 电子书下载

热门文章

  1. 液压支架销轴力学计算分析研究_技术 | 篦冷机液压管路问题分析及改造措施
  2. 技术岗的职业规划_数控工程系召开数控技术、数控维修专业建设指导委员会会议...
  3. android view选中状态,RecyclerView选中
  4. perl 字符串删除末尾几个字符_perl 第六弹 变量 II
  5. android ddmlib,通过 ddmlib 使用 adb,构建框架基础库
  6. springboot 没有找到service_Spring Boot 应用程序五种部署方式
  7. MySQl Modify JSON Values
  8. Linux 查看命令
  9. android智能手机发展,2011 LG Android智能手机发展蓝图
  10. Exchange Server 2016 独立部署/共存部署 (一)—— 前期准备