stream+springmvc实现文件断点续传

手上有个文件上传的需求,并且要支持断点续传最好要兼容性好一些,之前用过uploadify这个jquery上传插件,但是首先它不支持断点续传而且HTML5版本的竟然要收费,秉承中国特色这里就不予考虑了;于是在网上找到了一个叫 Stream的支持HTML5和Flash并且支持断点续传的这么一个插件,经过一天的尝试,终于把它整合到项目中去,现勉强能用了后续再优化优化应该能投入上线,废话不多上代码!
HTML+JS:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>断点续传</title>
<link rel="stylesheet" type="text/css" href="<%=basePath%>/statics/thirdparty/stream/css/stream-v1.css">
<script type="text/javascript" src="<%=basePath%>/statics/thirdparty/stream/js/stream-v1.js"></script>
</head>
<body>
<div id="i_select_files">
</div >
<div id="i_stream_files_queue">
</div>
<button onclick="javascript:_t.upload();">开始上传</button>|<button onclick="javascript:_t.stop();">停止上传</button>|<button onclick="javascript:_t.cancel();">取消</button>
|<button onclick="javascript:_t.disable();">禁用文件选择</button>|<button onclick="javascript:_t.enable();">启用文件选择</button>
|<button onclick="javascript:_t.hideBrowseBlock();">隐藏文件选择按钮</button>|<button onclick="javascript:_t.showBrowseBlock();">显示文件选择按钮</button>
|<button onclick="javascript:_t.destroy();_t=null;_t=new Stream(config);">销毁重新生成按钮</button>
<br>
Messages:
<div id="i_stream_message_container" class="stream-main-upload-box" style="overflow: auto;height:200px;">
</div>
</body>
<script type="text/javascript">
/**
* 配置文件(如果没有默认字样,说明默认值就是注释下的值)
* 但是,on*(onSelect, onMaxSizeExceed...)等函数的默认行为
* 是在ID为i_stream_message_container的页面元素中写日志
*/
var config = {
browseFileId : "i_select_files", /** 选择文件的ID, 默认: i_select_files */
browseFileBtn : "<div>请选择文件</div>", /** 显示选择文件的样式, 默认: `<div>请选择文件</div>` */
dragAndDropArea: "i_select_files", /** 拖拽上传区域,Id(字符类型"i_select_files")或者DOM对象, 默认: `i_select_files` */
dragAndDropTips: "<span>可以把文件(文件夹)拖拽到这里</span>", /** 拖拽提示, 默认: `<span>把文件(文件夹)拖拽到这里</span>` */
filesQueueId : "i_stream_files_queue", /** 文件上传容器的ID, 默认: i_stream_files_queue */
filesQueueHeight : 200, /** 文件上传容器的高度(px), 默认: 450 */
messagerId : "i_stream_message_container", /** 消息显示容器的ID, 默认: i_stream_message_container */
maxSize: 4294967296, /** 单个文件的最大大小,默认:2G */
multipleFiles: false, /** 多个文件一起上传, 默认: false */
autoUploading: true, /** 选择文件后是否自动上传, 默认: true */
// autoRemoveCompleted : true, /** 是否自动删除容器中已上传完毕的文件, 默认: false */
// retryCount : 5, /** HTML5上传失败的重试次数 */
// postVarsPerFile : { /** 上传文件时传入的参数,默认: {} */
// param1: "val1",
// param2: "val2"
// },
// swfURL : "/swf/FlashUploader.swf", /** SWF文件的位置 */
tokenURL : "<%=request.getContextPath()%>/tk", /** 根据文件名、大小等信息获取Token的URI(用于生成断点续传、跨域的令牌) */
// frmUploadURL : "/fd;", /** Flash上传的URI */
uploadURL : "<%=request.getContextPath()%>/upload", /** HTML5上传的URI */
// simLimit: 200, /** 单次最大上传文件个数 */
// extFilters: [".txt", ".rpm", ".rmvb", ".gz", ".rar", ".zip", ".avi", ".mkv", ".mp3"], /** 允许的文件扩展名, 默认: [] */
// onSelect: function(list) {alert('onSelect')}, /** 选择文件后的响应事件 */
onMaxSizeExceed: function(size, limited, name) {alert('文件已超过4G');}, /** 文件大小超出的响应事件 */
// onFileCountExceed: function(selected, limit) {alert('onFileCountExceed')}, /** 文件数量超出的响应事件 */
// onExtNameMismatch: function(name, filters) {alert('onExtNameMismatch')}, /** 文件的扩展名不匹配的响应事件 */
// onCancel : function(file) {alert('Canceled: ' + file.name)}, /** 取消上传文件的响应事件 */
// onComplete: function(file) {alert('onComplete')}, /** 单个文件上传完毕的响应事件 */
onQueueComplete: function() {
alert('onQueueComplete');
}, /** 所以文件上传完毕的响应事件 */
// onUploadError: function(status, msg) {alert('onUploadError')} /** 文件上传出错的响应事件 */
// onDestroy: function() {alert('onDestroy')} /** 文件上传出错的响应事件 */
};
var _t = new Stream(config);
</script>
</html>
页面代码精简了下,只留下了关键部分。
TokenServlet:
package com.dnion.oa.stream.servlet;
 
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.json.JSONException;
import org.json.JSONObject;
import com.dnion.oa.stream.util.IoUtil;
import com.dnion.oa.stream.util.TokenUtil;
import com.dnion.oa.utils.Configuration;
import com.dnion.oa.utils.Constant;
 
/**
* According the file name and its size, generate a unique token. And this
* token will be refer to user's file.
*/
public class TokenServlet extends HttpServlet {
private static final long serialVersionUID = 2650340991003623753L;
static final String FILE_NAME_FIELD = "name";
static final String FILE_SIZE_FIELD = "size";
static final String TOKEN_FIELD = "token";
static final String SERVER_FIELD = "server";
static final String SUCCESS = "success";
static final String MESSAGE = "message";
@Override
public void init() throws ServletException {
}
 
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String name = StringUtils.trimToEmpty(req.getParameter(FILE_NAME_FIELD));
String size = StringUtils.trimToEmpty(req.getParameter(FILE_SIZE_FIELD));
String token = TokenUtil.generateToken(name, size);
PrintWriter writer = resp.getWriter();
JSONObject json = new JSONObject();
try {
if (name.equals("")) {
json.put(SUCCESS, false);
json.put(MESSAGE, "文件名为空");
}else if (size.equals("")) {
json.put(SUCCESS, false);
json.put(MESSAGE, "文件大小为空");
}else{
json.put(TOKEN_FIELD, token);
if (Configuration.getInstance().getIsCrossed())
json.put(SERVER_FIELD, Configuration.getInstance().getCrossServer());
json.put(SUCCESS, true);
json.put(MESSAGE, "获取TOKEN成功");
}
} catch (JSONException e) {
e.printStackTrace();
}
/** TODO: save the token. */
writer.write(json.toString());
}
 
@Override
protected void doHead(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
super.doHead(req, resp);
}
 
@Override
public void destroy() {
super.destroy();
}
}
这是实现上传功能必须实现接口之一,看名字就知道基本功能就是获取token实现‘断点‘功能
StreamServlet:
package com.dnion.oa.stream.servlet;
 
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.json.JSONException;
import org.json.JSONObject;
import com.dnion.oa.stream.util.IoUtil;
import com.dnion.oa.utils.Configuration;
 
/**
* File reserved servlet, mainly reading the request parameter and its file
* part, stored it.
*/
public class StreamServlet extends HttpServlet {
private static final long serialVersionUID = -8619685235661387895L;
/** when the has increased to 10kb, then flush it to the hard-disk. */
static final int BUFFER_LENGTH = 10240;
static final String START_FIELD = "start";
public static final String CONTENT_RANGE_HEADER = "content-range";
@Override
public void init() throws ServletException {
}
/**
* Lookup where's the position of this file?
*/
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
doOptions(req, resp);
final String token = req.getParameter(TokenServlet.TOKEN_FIELD);
final String size = req.getParameter(TokenServlet.FILE_SIZE_FIELD);
final String fileName = req.getParameter(TokenServlet.FILE_NAME_FIELD);
final PrintWriter writer = resp.getWriter();
/** TODO: validate your token. */
JSONObject json = new JSONObject();
long start = 0;
boolean success = true;
String message = "";
try {
File f = IoUtil.getTokenedFile(token);
start = f.length();
/** file size is 0 bytes. */
if (token.endsWith("_0") && "0".equals(size) && 0 == start)
f.renameTo(IoUtil.getFile(fileName));
} catch (FileNotFoundException fne) {
message = "Error: " + fne.getMessage();
success = false;
} finally {
try {
if (success)
json.put(START_FIELD, start);
json.put(TokenServlet.SUCCESS, success);
json.put(TokenServlet.MESSAGE, message);
} catch (JSONException e) {}
writer.write(json.toString());
IoUtil.close(writer);
}
}
 
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
doOptions(req, resp);
final String token = req.getParameter(TokenServlet.TOKEN_FIELD);
final String fileName = req.getParameter(TokenServlet.FILE_NAME_FIELD);
Range range = IoUtil.parseRange(req);
OutputStream out = null;
InputStream content = null;
final PrintWriter writer = resp.getWriter();
/** TODO: validate your token. */
JSONObject json = new JSONObject();
long start = 0;
boolean success = true;
String message = "";
File f = IoUtil.getTokenedFile(token);
try {
if (f.length() != range.getFrom()) {
/** drop this uploaded data */
throw new StreamException(StreamException.ERROR_FILE_RANGE_START);
}
out = new FileOutputStream(f, true);
content = req.getInputStream();
int read = 0;
final byte[] bytes = new byte[BUFFER_LENGTH];
while ((read = content.read(bytes)) != -1)
out.write(bytes, 0, read);
start = f.length();
} catch (StreamException se) {
success = StreamException.ERROR_FILE_RANGE_START == se.getCode();
message = "Code: " + se.getCode();
} catch (FileNotFoundException fne) {
message = "Code: " + StreamException.ERROR_FILE_NOT_EXIST;
success = false;
} catch (IOException io) {
message = "IO Error: " + io.getMessage();
success = false;
} finally {
IoUtil.close(out);
IoUtil.close(content);
 
/** rename the file */
if (range.getSize() == start) {
/** fix the `renameTo` bug */
File dst = IoUtil.getFile(fileName);
dst.delete();
f.renameTo(dst);
System.out.println("TK: `" + token + "`, NE: `" + fileName + "`");
/** if `STREAM_DELETE_FINISH`, then delete it. */
if (Configuration.getInstance().getIsDeleteFinished()) {
dst.delete();
}
}
try {
if (success)
json.put(START_FIELD, start);
json.put(TokenServlet.SUCCESS, success);
json.put(TokenServlet.MESSAGE, message);
} catch (JSONException e) {}
writer.write(json.toString());
IoUtil.close(writer);
}
}
@Override
protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("application/json");
resp.setHeader("Access-Control-Allow-Headers", "Content-Range,Content-Type");
resp.setHeader("Access-Control-Allow-Origin", Configuration.getInstance().getCrossOrigins());
resp.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
}
 
@Override
public void destroy() {
super.destroy();
}
}
必须实现接口之一,也是最为主要的一个servlet
这两个接口其实就是对应了js代码中tokenURL和uploadURL,跳转关系在web.xml中定义,把下面这段加进去就行了,记得改路径
<servlet>
<servlet-name>TokenServlet</servlet-name>
<servlet-class>com.dnion.oa.stream.servlet.TokenServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>TokenServlet</servlet-name>
<url-pattern>/tk</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>StreamServlet</servlet-name>
<servlet-class>com.dnion.oa.stream.servlet.StreamServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>StreamServlet</servlet-name>
<url-pattern>/upload</url-pattern>
</servlet-mapping>
 
<servlet>
<servlet-name>FormDataServlet</servlet-name>
<servlet-class>com.dnion.oa.stream.servlet.FormDataServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>FormDataServlet</servlet-name>
<url-pattern>/fd</url-pattern>
</servlet-mapping>
基本参数保存在properties文件中
stream-config.properties:
# file stored repository (Chinese words need ASCII, help tool @http://tool.oschina.net/encode?type=3)
STREAM_FILE_REPOSITORY=
# when the file has uploaded, whether delete it.
STREAM_DELETE_FINISH=false
# this server whether allow other different domain[s] upload file to this server
STREAM_IS_CROSS=false
# allowed domain (PS: flash method need modifying the `crossdomain.xml`)
STREAM_CROSS_ORIGIN=*
# when Browser @http:www.A.com, the file will upload to @STREAM_CROSS_SERVER
STREAM_CROSS_SERVER=http://customers.duapp.com
TEMP_UPLOAD_PATH=E\:\\resumeUpload //暂时放在本地 测试用
Configuration.java读取配置文件
Configuration.java:
package com.dnion.oa.utils;
 
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
 
public class Configuration {
private static Configuration config;
/**
* 版本号
*/
private String version;
private String crossServer;
private String crossOrigins;
private String dnionTranscoderUrl;
private Boolean isDeleteFinished;
private Boolean isCrossed;
private String uploadPath;
private Configuration() {
}
 
public synchronized static Configuration getInstance() {
if (config == null) {
config = new Configuration();
config.init();
}
return config;
}
/**
* 初始化
*/
private void init() {
PropertiesConfiguration prop = new PropertiesConfiguration();
prop.setEncoding("utf-8");
try {
prop.load("props/stream-config.properties");
this.setCrossServer(prop.getString("STREAM_CROSS_SERVER", ""));
this.setCrossOrigins(prop.getString("STREAM_CROSS_ORIGIN", ""));
this.setDnionTranscoderUrl(prop.getString("DNION_TRANS_CODER_URL",
""));
this.setIsDeleteFinished(Boolean.valueOf(prop.getString(
"STREAM_DELETE_FINISH", "")));
this.setIsCrossed(Boolean.valueOf(prop.getString("STREAM_IS_CROSS",
"")));
this.setChownShellCmd(prop.getString("CHOWN_SHELL_CMD", ""));
this.setUploadPath(prop.getString("TEMP_UPLOAD_PATH", ""));
} catch (ConfigurationException e) {
e.printStackTrace();
}
}
 
public String getVersion() {
return version;
}
 
public void setVersion(String version) {
this.version = version;
}
 
...getter and setters
还有6对,别忘了
}
Maven依赖:
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20090211</version>
</dependency>
<dependency>
<groupId>commons-configuration</groupId>
<artifactId>commons-configuration</artifactId>
<version>1.9</version>
</dependency>
其他还有一些TokenUtil.java就不给出了,作者代码托管在oschina上,自己拉了看着改吧~
效果图稍候放出:



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/553808.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

UC浏览器怎么删除收藏历史?UC浏览器删除收藏历史的操作方法

uc浏览器怎么删除收藏历史?在使用的一个uc浏览器中&#xff0c;就会对浏览器中做删除收藏操作&#xff0c;小编告诉大家怎么删除收藏历史?在使用浏览器的时候&#xff0c;当时搜索的时候&#xff0c;也是是当时需要的&#xff0c;之后就不需要了&#xff0c;那么这个时候就可…

spring注解( @Autowired、@Qualifier、@Resource、@PostConstruct、@PreDestroy、 @Component、@Scope)-描述的比较清楚

概述&#xff1a;注释配置相对于 XML 配置具有很多的优势&#xff1a; 它可以充分利用 Java 的反射机制获取类结构信息&#xff0c;这些信息可以有效减少配置的工作。如使用 JPA 注释配置 ORM 映射时&#xff0c;我们就不需要指定 PO 的属性名、类型等信息&#xff0c;如果关系…

傲游浏览器能改字体吗 网页字体设置方法简述

将网页字体调整为喜欢的格式&#xff0c;在一定程度上也能提高用户的使用体验&#xff0c;因此傲游浏览器推出了个性化的“网页字体更改”功能。那么&#xff0c;该如何使用这一功能呢?下面小编就来简单介绍一下傲游浏览器网页字体的设置方法&#xff0c;感兴趣的朋友不妨多多…

QQ浏览器如何开启夜间模式 夜间模式使用技巧分享

在夜晚长时间且高亮度的使用了QQ浏览器后&#xff0c;眼睛不免会觉得干涩。而开启QQ浏览器中的黑暗模式&#xff0c;则可以在一定程度上缓解这一问题!那么&#xff0c;QQ浏览器要如何开启夜间模式呢?下面小编就来分享一下QQ浏览器夜间模式的使用技巧&#xff0c;感兴趣的朋友不…

使用jackson对Java对象与JSON字符串相互转换的一些总结

总结一下自己使用 jackson 处理对象与 JSON 之间相互转换的心得。jackson 是一个用 Java 编写的&#xff0c;用来处理 JSON 格式数据的类库&#xff0c;它速度非常快&#xff0c;目前来看使用很广泛&#xff0c;逐渐替代了 Gson 和 json-lib 。 如果直接引入 jar 包&#xff0…

火狐怎么放大页面?火狐浏览器页面放大技巧

和许多浏览器产品一样&#xff0c;火狐浏览器也支持用户对当前页面进行缩放。当然&#xff0c;不同浏览器产品&#xff0c;页面缩放功能隐藏的位置也各不相同!那么&#xff0c;火狐怎么放大页面呢?下面是小编分享的火狐浏览器页面放大技巧&#xff0c;感兴趣的朋友可不要错过了…

JSON和JS对象之间的互转

1. jQuery插件支持的转换方式 $.parseJSON( jsonstr ); //jQuery.parseJSON(jsonstr),可以将json字符串转换成json对象 2. 浏览器支持的转换方式(Firefox&#xff0c;chrome&#xff0c;opera&#xff0c;safari&#xff0c;ie9&#xff0c;ie8)等浏览器&#xff1a; JSON.pa…

森林中的三个小矮人

今天七小编分享经典童话故事《森林中的三个小矮人》&#xff0c;故事中两个小姑娘&#xff0c;一位心地善良&#xff0c;说话会吐出金子;一位丑陋坏心&#xff0c;说话会吐出蛤蟆;来看看怎么回事吧! 从前&#xff0c;一位死了妻子的男人和一位死了丈夫的女人结了婚&#xff0c…

转码与重定向的区别之于SpringMVC

使用转发时&#xff0c;JSP容器将使用一个内部的方法来调用目标页面&#xff0c;新的页面继续处理同一个请求&#xff0c;而浏览器将不会知道这个过程。 与之相反&#xff0c;重定向方式的含义是第一个页面通知浏览器发送一个新的页面请求。因此转发要比重定向更快,而且跳转到的…

Spring的ApplicationEvent的使用

Spring的ApplicationEvent的使用 Spring 3.0中提供了很多类似*Aware的类&#xff0c;其中ApplicationContextAware接口可以实现我们在初始化bean的时候给bean注入ApplicationConxt&#xff08;Spring上下文对象&#xff09;对象。ApplicationContextAware接口提供了publishEven…

谷歌浏览器地址栏记录怎么删除 Chrome浏览器地址栏记录清除方法

相信那些经常使用谷歌Chrome浏览器的朋友都清楚&#xff0c;这款浏览器软件会在地址栏保存大量的访问记录&#xff0c;其中也包括一些不想保存下来的!那么&#xff0c;谷歌浏览器地址栏记录要怎么删除呢?不清楚具体方法的朋友&#xff0c;还不赶紧收藏学习一下。 方法步骤 在…

使用Java的BlockingQueue实现生产者-消费者

BlockingQueue也是java.util.concurrent下的主要用来控制线程同步的工具。 BlockingQueue有四个具体的实现类,根据不同需求,选择不同的实现类 1、ArrayBlockingQueue&#xff1a;一个由数组支持的有界阻塞队列&#xff0c;规定大小的BlockingQueue,其构造函数必须带一个int参数…

win7系统怎么查看主板信息

win7系统怎么查看主板信息?主板是一台计算机最重要的硬件设备之一&#xff0c;它不仅是所有硬件系统的核心&#xff0c;也是机箱中最大的电路板。今天小编就给大家带来了win7系统查看主板信息的方法&#xff0c;一起来看看win7系统怎么查看主板信息的吧。 win7系统怎么查看主…

Mybatis判断int类型是否为空

症状 : 域名筛选在选择正式,测试的时候没问题,选择终止的时候筛选结果不对分析:正式:1测试:2终止:0不选:null选择终止的时候得到了和不选一样的结果,下图为sql判断语句,以前一直都是这么判断的没有出现过问题 错误所在:通过观察log打印的sql语句发现status0时上述条件是不成立的…

win7电脑蓝屏的解决方法

在我们使用电脑的过程中想必有很多人都会遇到蓝屏现象吧&#xff0c;蓝屏指的是操作系统在无法从一个系统错误中恢复过来时所显示的屏幕图像&#xff0c;导致蓝屏现象的原因有很多&#xff0c;今天我们来看看win7电脑蓝屏的解决方法吧。 win7电脑蓝屏的解决方法 1、今天带来的…

$.extend()和(function($){….})(jQuery)

1. JS中substring与substr的区别 之前在项目中用到substring方法&#xff0c;因为C#中也有字符串的截取方法Substring方法&#xff0c;当时也没有多想就误以为这两种方法的使用时一样的。这样就直接按照在C#中使用Substring的方式&#xff0c;直接在js中用了substring&#…

积米浏览器如何清除浏览数据

相信大家都知道&#xff0c;大多数浏览器都会自动缓存浏览数据&#xff0c;而缓存的数据多了也就会直接影响到网页的浏览速度&#xff0c;所以最好是能够经常或者定期去清除浏览数据。那么&#xff0c;如何清除浏览数据呢?下面&#xff0c;以积米浏览器为例&#xff0c;我们一…

Java文件保存与删除工具类

import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream;/*** * author ahomeeye* * 2012-7-26 下午3:50:50 文件操作工具类*/ public class FileUtil {// 保存文件public static void saveFile(String newsRootPath, String filename…

win7在网上邻居上看不到别的电脑如何解决

近期在网上看到很多用户询问说win7纯净版系统在网上邻居上看不到别的电脑是什么情况呢?一般情况下&#xff0c;我们可以通过网上邻居和别的电脑共享文件、图片、视频等等。可是有时候Win7系统在网上邻居上看不到别的电脑&#xff0c;也无法搜索到。有什么办法能解决此故障问题…

循环删除List集合的错误

症状:不是郝柱也能看到灾备分析:调试发现动作中有两个灾备,不过只过滤了其中一个 错误所在:代码如下,这里for循环删除List逻辑出问题了,犯了一个比较基础的错误 : 两个灾备动作索引是相邻的,当我remove掉第一个时紧跟着的动作就会取代它原来的位置,最终导致问题发生 修改方案:解…