Servlet – Upload、Download、Async、动态注册

Upload-上传
随着3.0版本的发布,文件上传终于成为Servlet规范的一项内置特性,不再依赖于像Commons FileUpload之类组件,因此在服务端进行文件上传编程变得不费吹灰之力.

客户端
要上传文件, 必须利用multipart/form-data设置HTML表单的enctype属性,且method必须为POST:
<form action="simple_file_upload_servlet.do" method="POST" enctype="multipart/form-data">
<table align="center" border="1" width="50%">
<tr>
<td>Author:</td>
<td><input type="text" name="author"></td>
</tr>
<tr>
<td>Select file to Upload:</td>
<td><input type="file" name="file"></td>
</tr>
<tr>
<td><input type="submit" value="上传"></td>
</tr>
</table>
</form>
服务端
服务端Servlet主要围绕着@MultipartConfig注解和Part接口:

处理上传文件的Servlet必须用@MultipartConfig注解标注:

@MultipartConfig属性 描述
fileSizeThreshold The size threshold after which the file will be written to disk
location The directory location where files will be stored
maxFileSize The maximum size allowed for uploaded files.
maxRequestSize The maximum size allowed for multipart/form-data requests
在一个由多部件组成的请求中, 每一个表单域(包括非文件域), 都会被封装成一个Part,HttpServletRequest中提供如下两个方法获取封装好的Part:

HttpServletRequest 描述
Part getPart(String name) Gets the Part with the given name.
Collection<Part> getParts() Gets all the Part components of this request, provided that it is of type multipart/form-data.
Part中提供了如下常用方法来获取/操作上传的文件/数据:

Part 描述
InputStream getInputStream() Gets the content of this part as an InputStream
void write(String fileName) A convenience method to write this uploaded item to disk.
String getSubmittedFileName() Gets the file name specified by the client(需要有Tomcat 8.x 及以上版本支持)
long getSize() Returns the size of this fille.
void delete() Deletes the underlying storage for a file item, including deleting any associated temporary disk file.
String getName() Gets the name of this part
String getContentType() Gets the content type of this part.
Collection<String> getHeaderNames() Gets the header names of this Part.
String getHeader(String name) Returns the value of the specified mime header as a String.
文件流解析
通过抓包获取到客户端上传文件的数据格式:

------WebKitFormBoundaryXJ6TxfJ9PX5hJHGh
Content-Disposition: form-data; name="author"

feiqing
------WebKitFormBoundaryXJ6TxfJ9PX5hJHGh
Content-Disposition: form-data; name="file"; filename="memcached.txt"
Content-Type: text/plain

------WebKitFormBoundaryXJ6TxfJ9PX5hJHGh--
可以看到:
A. 如果HTML表单输入项为文本(<input type="text"/>),将只包含一个请求头Content-Disposition.
B. 如果HTML表单输入项为文件(<input type="file"/>), 则包含两个头:
Content-Disposition与Content-Type.
在Servlet中处理上传文件时, 需要:

<code>- 通过查看是否存在Content-Type标头, 检验一个Part是封装的普通表单域,还是文件域. - 若有Content-Type存在, 但文件名为空, 则表示没有选择要上传的文件. - 如果有文件存在, 则可以调用write()方法来写入磁盘, 调用同时传递一个绝对路径, 或是相对于@MultipartConfig注解的location属性的相对路径. </code>
SimpleFileUploadServlet

/**

  • @author jifang.
  • @since 2016/5/8 16:27.br/>*/
    @MultipartConfig
    @WebServlet(name = "SimpleFileUploadServlet", urlPatterns = "/simple_file_upload_servlet.do")
    public class SimpleFileUploadServlet extends HttpServlet {

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.setContentType("text/html;charset=UTF-8");
    PrintWriter writer = response.getWriter();
    Part file = request.getPart("file");
    if (!isFileValid(file)) {
    writer.print("<h1>请确认上传文件是否正确!");
    } else {
    String fileName = file.getSubmittedFileName();
    String saveDir = getServletContext().getRealPath("/WEB-INF/files/");
    mkdirs(saveDir);
    file.write(saveDir + fileName);

        writer.print("<h3>Uploaded file name: " + fileName);
        writer.print("<h3>Size: " + file.getSize());
        writer.print("<h3>Author: " + request.getParameter("author"));
    }

    }

    private void mkdirs(String saveDir) {
    File dir = new File(saveDir);
    if (!dir.exists()) {
    dir.mkdirs();
    }
    }

    private boolean isFileValid(Part file) {
    // 上传的并非文件
    if (file.getContentType() == null) {
    return false;
    }
    // 没有选择任何文件
    else if (Strings.isNullOrEmpty(file.getSubmittedFileName())) {
    return false;
    }
    return true;
    }
    }
    优化
    善用WEB-INF
    存放在/WEB-INF/目录下的资源无法在浏览器地址栏直接访问, 利用这一特点可将某些受保护资源存放在WEB-INF目录下, 禁止用户直接访问(如用户上传的可执行文件,如JSP等),以防被恶意执行, 造成服务器信息泄露等危险.

getServletContext().getRealPath("/WEB-INF/")
文件名乱码
当文件名包含中文时,可能会出现乱码,其解决方案与POST相同:
1
request.setCharacterEncoding("UTF-8");
避免文件同名
如果上传同名文件,会造成文件覆盖.因此可以为每份文件生成一个唯一ID,然后连接原始文件名:

private String generateUUID() {
return UUID.randomUUID().toString().replace("-", "_");
}
目录打散
如果一个目录下存放的文件过多, 会导致文件检索速度下降,因此需要将文件打散存放到不同目录中, 在此我们采用Hash打散法(根据文件名生成Hash值, 取Hash值的前两个字符作为二级目录名), 将文件分布到一个二级目录中:
1
2
3
4
private String generateTwoLevelDir(String destFileName) {
String hash = Integer.toHexString(destFileName.hashCode());
return String.format("%s/%s", hash.charAt(0), hash.charAt(1));
}
采用Hash打散的好处是:在根目录下最多生成16个目录,而每个子目录下最多再生成16个子子目录,即一共256个目录,且分布较为均匀.

示例-简易存储图片服务器
需求: 提供上传图片功能, 为其生成外链, 并提供下载功能(见下)

客户端

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>IFS</title>
</head>
<body>
<form action="ifs_upload.action" method="POST" enctype="multipart/form-data">
<table align="center" border="1" width="50%">
<tr>
<td>Select A Image to Upload:</td>
<td><input type="file" name="image"></td>
</tr>
<tr>
<td> </td>
<td><input type="submit" value="上传"></td>br/>

服务端
@MultipartConfig
@WebServlet(name = "ImageFileUploadServlet", urlPatterns = "/ifs_upload.action")
public class ImageFileUploadServlet extends HttpServlet {

private Set<String> imageSuffix = new HashSet<>();

private static final String SAVE_ROOT_DIR = "/images";

{
    imageSuffix.add(".jpg");
    imageSuffix.add(".png");
    imageSuffix.add(".jpeg");
}

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    request.setCharacterEncoding("UTF-8");
    response.setContentType("text/html;charset=UTF-8");
    PrintWriter writer = response.getWriter();
    Part image = request.getPart("image");
    String fileName = getFileName(image);
    if (isFileValid(image, fileName) && isImageValid(fileName)) {
        String destFileName = generateDestFileName(fileName);
        String twoLevelDir = generateTwoLevelDir(destFileName);

        // 保存文件
        String saveDir = String.format("%s/%s/", getServletContext().getRealPath(SAVE_ROOT_DIR), twoLevelDir);
        makeDirs(saveDir);
        image.write(saveDir + destFileName);

        // 生成外链
        String ip = request.getLocalAddr();
        int port = request.getLocalPort();
        String path = request.getContextPath();
        String urlPrefix = String.format("http://%s:%s%s", ip, port, path);
        String urlSuffix = String.format("%s/%s/%s", SAVE_ROOT_DIR, twoLevelDir, destFileName);
        String url = urlPrefix + urlSuffix;
        String result = String.format("<a href=%s>%s</a><hr/><a href=ifs_download.action?location=%s>下载</a>",
                url,
                url,
                saveDir + destFileName);
        writer.print(result);
    } else {
        writer.print("Error : Image Type Error");
    }
}

/**
 * 校验文件表单域有效
 *
 * @param file
 * @param fileName
 * @return
 */
private boolean isFileValid(Part file, String fileName) {
    // 上传的并非文件
    if (file.getContentType() == null) {
        return false;
    }
    // 没有选择任何文件
    else if (Strings.isNullOrEmpty(fileName)) {
        return false;
    }

    return true;
}

/**
 * 校验文件后缀有效
 *
 * @param fileName
 * @return
 */
private boolean isImageValid(String fileName) {
    for (String suffix : imageSuffix) {
        if (fileName.endsWith(suffix)) {
            return true;
        }
    }
    return false;
}

/**
 * 加速图片访问速度, 生成两级存放目录
 *
 * @param destFileName
 * @return
 */
private String generateTwoLevelDir(String destFileName) {
    String hash = Integer.toHexString(destFileName.hashCode());
    return String.format("%s/%s", hash.charAt(0), hash.charAt(1));
}

private String generateUUID() {
    return UUID.randomUUID().toString().replace("-", "_");
}

private String generateDestFileName(String fileName) {
    String destFileName = generateUUID();
    int index = fileName.lastIndexOf(".");
    if (index != -1) {
        destFileName += fileName.substring(index);
    }
    return destFileName;
}

private String getFileName(Part part) {
    String[] elements = part.getHeader("content-disposition").split(";");
    for (String element : elements) {
        if (element.trim().startsWith("filename")) {
            return element.substring(element.indexOf("=") + 1).trim().replace("\"", "");
        }
    }
    return null;
}

private void makeDirs(String saveDir) {
    File dir = new File(saveDir);
    if (!dir.exists()) {
        dir.mkdirs();
    }
}

}
由于getSubmittedFileName()方法需要有Tomcat 8.X以上版本的支持, 因此为了通用期间, 我们自己解析content-disposition请求头, 获取filename.

Download-下载
文件下载是向客户端响应二进制数据(而非字符),浏览器不会直接显示这些内容,而是会弹出一个下载框, 提示下载信息.

为了将资源发送给浏览器, 需要在Servlet中完成以下工作:

使用Content-Type响应头来规定响应体的MIME类型, 如image/pjpeg、application/octet-stream;
添加Content-Disposition响应头,赋值为attachment;filename=xxx.yyy, 设置文件名;
使用response.getOutputStream()给浏览器发送二进制数据;
文件名中文乱码
当文件名包含中文时(attachment;filename=文件名.后缀名),在下载框中会出现乱码, 需要对文件名编码后在发送, 但不同的浏览器接收的编码方式不同:

<code> * FireFox: Base64编码

  • 其他大部分Browser: URL编码 </code>
    因此最好将其封装成一个通用方法:

private String filenameEncoding(String filename, HttpServletRequest request) throws IOException {
// 根据浏览器信息判断
if (request.getHeader("User-Agent").contains("Firefox")) {
filename = String.format("=?utf-8?B?%s?=", BaseEncoding.base64().encode(filename.getBytes("UTF-8")));
} else {
filename = URLEncoder.encode(filename, "utf-8");
}
return filename;
}
示例-IFS下载功能
/**

  • @author jifang.
  • @since 2016/5/9 17:50.
    */
    @WebServlet(name = "ImageFileDownloadServlet", urlPatterns = "/ifs_download.action")
    public class ImageFileDownloadServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.setContentType("application/octet-stream");
    String fileLocation = request.getParameter("location");
    String fileName = fileLocation.substring(fileLocation.lastIndexOf("/") + 1);
    response.setHeader("Content-Disposition", "attachment;filename=" + filenameEncoding(fileName, request));

    ByteStreams.copy(new FileInputStream(fileLocation), response.getOutputStream());

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    doGet(req, resp);
    }
    }
    Async-异步处理
    Servlet/Filter默认会一直占用请求处理线程, 直到它完成任务.如果任务耗时长久, 且并发用户请求量大, Servlet容器将会遇到超出线程数的风险.

Servlet 3.0 中新增了一项特性, 用来处理异步操作. 当Servlet/Filter应用程序中有一个/多个长时间运行的任务时, 你可以选择将任务分配给一个新的线程, 从而将当前请求处理线程返回到线程池中,释放线程资源,准备为下一个请求服务.

异步Servlet/Filter
异步支持
@WebServlet/@WebFilter注解提供了新的asyncSupport属性:

@WebFilter(asyncSupported = true)
@WebServlet(asyncSupported = true)
同样部署描述符中也添加了<async-supportted/>标签:

<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.fq.web.servlet.HelloServlet</servlet-class>
<async-supported>true</async-supported>
</servlet>
Servlet/Filter
支持异步处理的Servlet/Filter可以通过在ServletRequest中调用startAsync()方法来启动新线程:
ServletRequest 描述
AsyncContext startAsync() Puts this request into asynchronous mode, and initializes its AsyncContext with the original (unwrapped) ServletRequest and ServletResponse objects.
AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) Puts this request into asynchronous mode, and initializes its AsyncContext with the given request and response objects.
注意:

  1. 只能将原始的ServletRequest/ServletResponse或其包装器(Wrapper/Decorator,详见Servlet – Listener、Filter、Decorator)传递给第二个startAsync()方法.
  2. 重复调用startAsync()方法会返回相同的AsyncContext实例, 如果在不支持异步处理的Servlet/Filter中调用, 会抛出java.lang.IllegalStateException异常.
  3. AsyncContext的start()方法不会造成方法阻塞.

这两个方法都返回AsyncContext实例, AsyncContext中提供了如下常用方法:

AsyncContext 描述
void start(Runnable run) Causes the container to dispatch a thread, possibly from a managed thread pool, to run the specified Runnable.
void dispatch(String path) Dispatches the request and response objects of this AsyncContext to the given path.
void dispatch(ServletContext context, String path) Dispatches the request and response objects of this AsyncContext to the given path scoped to the given context.
void addListener(AsyncListener listener) Registers the given AsyncListener with the most recent asynchronous cycle that was started by a call to one of the ServletRequest.startAsync() methods.
ServletRequest getRequest() Gets the request that was used to initialize this AsyncContext by calling ServletRequest.startAsync() or ServletRequest.startAsync(ServletRequest, ServletResponse).
ServletResponse getResponse() Gets the response that was used to initialize this AsyncContext by calling ServletRequest.startAsync() or ServletRequest.startAsync(ServletRequest, ServletResponse).
boolean hasOriginalRequestAndResponse() Checks if this AsyncContext was initialized with the original or application-wrapped request and response objects.
void setTimeout(long timeout) Sets the timeout (in milliseconds) for this AsyncContext.
在异步Servlet/Filter中需要完成以下工作, 才能真正达到异步的目的:

调用AsyncContext的start()方法, 传递一个执行长时间任务的Runnable;
任务完成时, 在Runnable内调用AsyncContext的complete()方法或dispatch()方法
示例-改造文件上传
在前面的图片存储服务器中, 如果上传图片过大, 可能会耗时长久,为了提升服务器性能, 可将其改造为异步上传(其改造成本较小):

@Override
protected void doPost(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
final AsyncContext asyncContext = request.startAsync();
asyncContext.start(new Runnable() {
br/>@Override
public void run() {
try {
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
PrintWriter writer = response.getWriter();
Part image = request.getPart("image");
final String fileName = getFileName(image);
if (isFileValid(image, fileName) && isImageValid(fileName)) {
String destFileName = generateDestFileName(fileName);
String twoLevelDir = generateTwoLevelDir(destFileName);

                // 保存文件
                String saveDir = String.format("%s/%s/", getServletContext().getRealPath(SAVE_ROOT_DIR), twoLevelDir);
                makeDirs(saveDir);
                image.write(saveDir + destFileName);
                // 生成外链
                String ip = request.getLocalAddr();
                int port = request.getLocalPort();
                String path = request.getContextPath();
                String urlPrefix = String.format("http://%s:%s%s", ip, port, path);
                String urlSuffix = String.format("%s/%s/%s", SAVE_ROOT_DIR, twoLevelDir, destFileName);
                String url = urlPrefix + urlSuffix;
                String result = String.format("<a href=%s>%s</a><hr/><a href=ifs_download.action?location=%s>下载</a>",
                        url,
                        url,
                        saveDir + destFileName);
                writer.print(result);
            } else {
                writer.print("Error : Image Type Error");
            }
            asyncContext.complete();
        } catch (ServletException | IOException e) {
            LOGGER.error("error: ", e);
        }
    }
});

}
注意: Servlet异步支持只适用于长时间运行,且想让用户知道执行结果的任务. 如果只有长时间, 但用户不需要知道处理结果,那么只需提供一个Runnable提交给Executor, 并立即返回即可.

AsyncListener
Servlet 3.0 还新增了一个AsyncListener接口, 以便通知用户在异步处理期间发生的事件, 该接口会在异步操作的启动/完成/失败/超时情况下调用其对应方法:

ImageUploadListener
/**

  • @author jifang.
  • @since 2016/5/10 17:33.
    */
    public class ImageUploadListener implements AsyncListener {

    @Override
    public void onComplete(AsyncEvent event) throws IOException {
    System.out.println("onComplete...");
    }

    @Override
    public void onTimeout(AsyncEvent event) throws IOException {
    System.out.println("onTimeout...");
    }

    @Override
    public void onError(AsyncEvent event) throws IOException {
    System.out.println("onError...");
    }

    @Override
    public void onStartAsync(AsyncEvent event) throws IOException {
    System.out.println("onStartAsync...");
    }
    }
    与其他监听器不同, 他没有@WebListener标注AsyncListener的实现, 因此必须对有兴趣收到通知的每个AsyncContext都手动注册一个AsyncListener:

1
asyncContext.addListener(new ImageUploadListener());
动态注册
动态注册是Servlet 3.0新特性,它不需要重新加载应用便可安装新的Web对象(Servlet/Filter/Listener等).

API支持
为了使动态注册成为可能, ServletContext接口添加了如下方法用于 创建/添加 Web对象:

ServletContext 描述
Create
<T extends Servlet> T createServlet(Class<T> clazz) Instantiates the given Servlet class.
<T extends Filter> T createFilter(Class<T> clazz) Instantiates the given Filter class.
<T extends EventListener> T createListener(Class<T> clazz) Instantiates the given EventListener class.
Add
ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet) Registers the given servlet instance with this ServletContext under the given servletName.
FilterRegistration.Dynamic addFilter(String filterName, Filter filter) Registers the given filter instance with this ServletContext under the given filterName.
<T extends EventListener> void addListener(T t) Adds the given listener to this ServletContext.
Create & And
ServletRegistration.Dynamic addServlet(String servletName, Class<? extends Servlet> servletClass) Adds the servlet with the given name and class type to this servlet context.
ServletRegistration.Dynamic addServlet(String servletName, String className) Adds the servlet with the given name and class name to this servlet context.
FilterRegistration.Dynamic addFilter(String filterName, Class<? extends Filter> filterClass) Adds the filter with the given name and class type to this servlet context.
FilterRegistration.Dynamic addFilter(String filterName, String className) Adds the filter with the given name and class name to this servlet context.
void addListener(Class<? extends EventListener> listenerClass) Adds a listener of the given class type to this ServletContext.
void addListener(String className) Adds the listener with the given class name to this ServletContext.
其中addServlet()/addFilter()方法的返回值是ServletRegistration.Dynamic/FilterRegistration.Dynamic,他们都是Registration.Dynamic的子接口,用于动态配置Servlet/Filter实例.

示例-DynamicServlet
动态注册DynamicServlet, 注意: 并未使用web.xml或@WebServlet静态注册DynamicServlet实例, 而是用DynRegListener在服务器启动时动态注册.

DynamicServlet

/**

  • @author jifang.
  • @since 2016/5/13 16:41.
    */
    public class DynamicServlet extends HttpServlet {

    private String dynamicName;

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.getWriter().print("<h1>DynamicServlet, MyDynamicName: " + getDynamicName() + "</h1>");
    }

    public String getDynamicName() {
    return dynamicName;
    }

    public void setDynamicName(String dynamicName) {
    this.dynamicName = dynamicName;
    }
    }
    DynRegListener

@WebListener
public class DynRegListener implements ServletContextListener {

@Override
public void contextInitialized(ServletContextEvent sce) {
    ServletContext context = sce.getServletContext();

    DynamicServlet servlet;
    try {
        servlet = context.createServlet(DynamicServlet.class);
    } catch (ServletException e) {
        servlet = null;
    }

    if (servlet != null) {
        servlet.setDynamicName("Hello fQ Servlet");
        ServletRegistration.Dynamic dynamic = context.addServlet("dynamic_servlet", servlet);
        dynamic.addMapping("/dynamic_servlet.do");
    }

}

@Override
public void contextDestroyed(ServletContextEvent sce) {
}

}
容器初始化
在使用类似SpringMVC这样的MVC框架时,需要首先注册DispatcherServlet到web.xml以完成URL的转发映射:

mvc
org.springframework.web.servlet.DispatcherServlet

contextConfigLocation
classpath:spring/mvc-servlet.xml

1

mvc
*.do

在Servlet 3.0中,通过Servlet容器初始化,可以自动完成Web对象的首次注册,因此可以省略这个步骤.
API支持
容器初始化的核心是javax.servlet.ServletContainerInitializer接口,他只包含一个方法:
ServletContainerInitializer 描述
void onStartup(Set> c, ServletContext ctx) Notifies this ServletContainerInitializer of the startup of the application represented by the given ServletContext.
在执行任何ServletContext监听器之前, 由Servlet容器自动调用onStartup()方法.
注意: 任何实现了ServletContainerInitializer的类必须使用@HandlesTypes注解标注, 以声明该初始化程序可以处理这些类型的类.
实例-SpringMVC初始化
利用Servlet容器初始化, SpringMVC可实现容器的零配置注册.
SpringServletContainerInitializer
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(Set> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List initializers = new LinkedList();
if (webAppInitializerClasses != null) {
for (Class waiClass : webAppInitializerClasses) {
// Be defensive: Some servlet containers provide us with invalid classes,
// no matter what @HandlesTypes says...
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
initializers.add((WebApplicationInitializer) waiClass.newInstance());
}
catch (Throwable ex) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
}
}
}
}
if (initializers.isEmpty()) {
servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
return;
}
AnnotationAwareOrderComparator.sort(initializers);
servletContext.log("Spring WebApplicationInitializers detected on classpath: " + initializers);
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
}
}
SpringMVC为ServletContainerInitializer提供了实现类SpringServletContainerInitializer通过查看源代码可以知道,我们只需提供WebApplicationInitializer的实现类到classpath下, 即可完成对所需Servlet/Filter/Listener的注册.
public interface WebApplicationInitializer {
void onStartup(ServletContext servletContext) throws ServletException;
}
详细可参考springmvc基于java config的实现
javax.servlet.ServletContainerInitializer
1
org.springframework.web.SpringServletContainerInitializer
元数据文件javax.servlet.ServletContainerInitializer只有一行内容(即实现了ServletContainerInitializer类的全限定名),该文本文件必须放在jar包的META-INF/services目录下

Servlet – Upload、Download、Async、动态注册

原文地址:http://blog.51cto.com/13952955/2296045

时间: 2024-08-10 05:30:37

Servlet – Upload、Download、Async、动态注册的相关文章

第十六章_动态注册和Servlet容器初始化

16.1.动态注册 为了使动态注册成为可能,ServletContext接口中还添加了以下方法,用来动态地创建Web对象: <T extends Filter>createFilter(java.lang.Class<T> clazz) <T extends java.util.EventListener> createListener(java.lang.Class<T> clazz) <T extends Servlet> createSer

Asp.net core 学习笔记 ( upload/download files 文件上传与下载 )

2017-09-25 refer : https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads https://www.codeproject.com/Articles/1203408/Upload-Download-Files-in-ASP-NET-Core 这里只说说小文件上传. 先看看前端 js 代码 <input id="inputFile" type="file" /&g

Android so lib库远程http下载和动态注册

一.背景 在开发Android应用程序的实现,有时候需要引入第三方so lib库,但第三方so库比较大,例如开源第三方播放组件ffmpeg库, 如果直接打包的apk包里面, 整个应用程序会大很多.经过查阅资料和实验,发现通过远程下载so文件,然后再动态注册so文件时可行的.主要需要解决下载so文件存放位置以及文件读写权限问题. 二.主要思路   1.首先把so放到网络上面,比如测试放到:http://codestudy.sinaapp.com/lib/test.so 2.应用启动时,启动异步线程

自己动手写RPC框架到dubbo的服务动态注册,服务路由,负载均衡功能实现

RPC即远程过程调用,它的实现方式有很多,比如webservice等.框架调多了,烦了,没激情了,我们就该问自己,这些框架的作用到底是什么,来找回当初的激情. 一般来说,我们写的系统就是一个单机系统,一个web服务器一个数据库服务,但是当这单台服务器的处理能力受硬件成本的限制,是不能无限的提升处理性能的.这个时候我们使用RPC将原来的本地调用转变为调用远端的服务器上的方法,给系统的处理能力和吞吐量带来了提升. RPC的实现包括客户端和服务端,即服务的调用方和服务的提供方.服务调用方发送rpc请求

动态注册和注销BroadercasterReceive(二)

有时候不需要监听广播是否有消息,所以可以使用动态注册的方式.即不需要再androidmanifest.xml中注册. 主要代码如下: package com.example.yabushan.broadercastreceiver; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; public class MyReceiver exte

在Asp.net 4.0 中动态注册HttpModule

using System; using System.Web; using Microsoft.Web.Infrastructure; namespace MvcApplication1 { public class CustomModule : IHttpModule { public void Init(HttpApplication context) { context.BeginRequest += new EventHandler(context_BeginRequest); } vo

BroadcastReceiver 的动态注册和静态注册问题

最近开发的小项目涉及到notification通知栏的交互问题,通知栏的各种点击事件响应通过使用broadcast实现,由于一开始没有想到使用handler提交通知栏执行后的状态或者说是信息.一直纠结与怎么注册broadcast的问题,这里先说一下注册的事情. 众所周知,注册可以在manifest.xml里面注册,还有就是在代码里面注册. 在manifest里面的注册形式是这样子的: <receiver android:name=".继承broadcastReceiver的class&qu

MVC之前的那点事儿系列(6):动态注册HttpModule(转载)

MVC之前的那点事儿系列(6):动态注册HttpModule 文章内容 通过前面的章节,我们知道HttpApplication在初始化的时候会初始化所有配置文件里注册的HttpModules,那么有一个疑问,能否初始化之前动态加载HttpModule,而不是只从Web.config里读取? 答案是肯定的, ASP.NET MVC3发布的时候提供了一个Microsoft.Web.Infrastructure.dll文件,这个文件就是提供了动态注册HttpModule的功能,那么它是如何以注册的呢?

Android 系统广播动态注册

package com.broadcasttest; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.view.View;