[转载]从image/x-png谈ContentType(s) - 执手泪眼 - 博客园

mikel阅读(977)

[转载]从image/x-png谈ContentType(s) – 执手泪眼 – 博客园.

今天在做一个文件上传的功能的时候,发现我总是上传不了png的图片。经过调试发现,应该是在ContentType的地方判断失误了。后来百度了 一下发现一个有意思的现象,我发现png的图片的ContentType并不是我在注册表中看到的image/png,而是image/x-png。也因 此造成了上传不了png文件的原因(后来看了资料才知道我找错了注册表的位置)。经过自己测试,得出的结果如下:

      IE6 IE 8.0 Chrome Firefox
png image/x-png image/x-png image/png image/png
jpg image/pjpeg image/jpeg image/jpeg image/jpeg
jpeg image/pjpeg image/pjpeg image/jpeg image/jpeg
bmp image/bmp  image/bmp image/bmp image/bmp

相比较而言,Chrome和FireFox的反映出的ContentType算是比较符合真实的答案的。那么,为什么会有这些差异呢?

 

  1. 首先来说一下MIME类型:

MIME类型就是设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打开。多用于指定一些客户端自定义的文件名,以及一些媒体文件打开方式。

 

MIME的英文全称是”Multipurpose Internet Mail Extensions” 多功能Internet 邮件扩充服务,它是一种多用途网际邮件扩充协议,在1992年最早应用于电子邮件系统,但后来也应用到浏览器。服务器会将它们发送的多媒体数据的类型告诉 浏览器,而通知手段就是说明该多媒体数据的MIME类型,从而让浏览器知道接收到的信息哪些是MP3文件,哪些是Shockwave文件等等。服务器将 MIME标志符放入传送的数据中来告诉浏览器使用哪种插件读取相关文件。

 

MIME能够支持非ASCII字符、二进制格式附件等多种格式的邮件消息。这个标准被定义在; RFC 2045,; RFC 2046,; RFC 2047,; RFC 2048,; RFC 2049等RFC中。 由RFC 822转变而来的RFC 2822,规定电子邮件标准并不允许在邮件消息中使用7位ASCII字符集以外的字符。正因如此,一些非英语字符消息和二进制文件,图像,声音等非文字消 息都不能在电子邮件中传输。MIME规定了用于表示各种各样的数据类型的符号化方法。

 

浏览器接收到文件后,会进入插件系统进行查找,查找出哪种插件可以识别读取接收到的文件。如果浏览器不清楚调用哪种插件系统,它可能会告诉用户 缺少某插件,或者直接选择某现有插件来试图读取接收到的文件,后者可能会导致系统的崩溃。传输的信息中缺少MIME标识可能导致的情况很难估计,因为某些 计算机系统可能不会出现什么故障,但某些计算机可能就会因此而崩溃。

 

在把输出结果传送到浏览器上的时候,浏览器必须启动适当的应用程序来处理这个输出文档。这可以通过多种类型MIME(多功能网际邮件扩充协议)来完成。在HTTP中,MIME类型被定义在Content-Type header中。

例如,假设你要传送一个Microsoft Excel文件到客户端。那么这时的MIME类型就是“application/vnd.ms-excel”。在大多数实际情况中,这个文件然后将传送给 Execl来处理(假设我们设定Excel为处理特殊MIME类型的应用程序)。在ASP中,设定MIME类型的方法是通过Response对象的 ContentType属性。

 

  1. IE中对MIME类型的解析:

在Microsoft Internet Explorer 4.0及更高版本,MIME类型确定发生在通过URL Moniker的 FindMimeFromData 方法。 确定的MIME类型允许URL名字对象和其他组件来查找并启动正确的对象服务器或应用程序来处理相关的内容。

 

The server-supplied MIME type, if available

An examination of the actual contents associated with a downloaded URL

The file name associated with the downloaded content (assumed to be derived from the associated URL)

Registry settings (file name extension/MIME type associations or registered applications) in effect during the download

 

 

Registry Locations

Location used by FindMimeFromData to find MIME type and progID from file name extension:

HKEY_CLASSES_ROOT\.***
Location used by FindMimeFromData to find application from progID:

HKEY_CLASSES_ROOT\<ProgId>\shell\open\command
Location used by URL monikers to find CLSIDs from MIME types:

HKEY_CLASSES_ROOT\MIME\Database\Content Type

The purpose of MIME type detection, or data sniffing, is to determine the MIME type (also known as content type or media type) of downloaded content using information from the following four sources:

 

 

关于具体的资料可以参考MSDN的文档:

http://msdn.microsoft.com/zh-cn/library/ms775147(v=VS.85).aspx中关于MIME类型的检测算法、和FindMimeFromData函数以及Uploaded MIME Types章节的内容。

 

  1. 为什么image/x-png前面有个x-:

Internet中有一个专门组织IANA来确认标准的MIME类型,但Internet发展的太快,很多应用程序等不及IANA来确认他们使用的 MIME类型为标准类型。因此他们使用在类别中以x-开头的方法标识这个类别还没有成为标准,例如:x-gzip,x-tar等。事实上这些类型运用的很 广泛,已经成为了事实标准。只要客户机和服务器共同承认这个MIME类型,即使它是不标准的类型也没有关系,客户程序就能根据MIME类型,采用具体的处 理手段来处理数据。而Web服务器和浏览器(包括操作系统)中,缺省都设置了标准的和常见的MIME类型,只有对于不常见的 MIME类型,才需要同时设置服务器和客户浏览器,以进行识别。

这里有一个关于image/x-png的讨论http://stackoverflow.com/questions/2086374/what-is-the-difference-between-image-png-and-image-x-png.

[转载]用代理方式实现android系统抓包 - 数据分析点滴 - 博客频道 - CSDN.NET

mikel阅读(1119)

[转载]用代理方式实现android系统抓包 – 数据分析点滴 – 博客频道 – CSDN.NET.

调试Android应用的网络交互时,常常希望看到网络包的交互过程。而在破解app时也经常希望看到网络交互过程的抓包。之前的思路一直是在手机 上安装抓包软件,这种方式实时性不好而且分析能力有限。有天看到测试组的MM用一个简洁的方式解决的这个问题,很巧妙,特记录下分享。

1 设置手机上的代理到pc电脑的端口8888

可以再系统中设置。有个软件是autoproxy可以设置多个代理的切换,很好用(前提是你有su权限)

2 在pc安装代理抓包一体的fiddle

fiddle是一款作为代理并能显示经过代理的网络包的软件,很实用。安装好以后自动监听8888端口。默认设置只是作为本地的代理,需要在tools/fiddle option/connection里面允许远端的机器访问该代理。设置成功后重启fiddle。

3. 在手机上开启代理后访问,就可以在fiddle上显示出来了。

ps:调试可用性时,可以直接用http请求你的代理端口,这是一个echo服务,会有一些相应,方便的确定手机到代理的网络是不是有问题。

[转载]Android 使用ContentProvider扫描手机中的图片,仿微信显示本地图片效果 - Mobile Internet developer - 博客频道 - CSDN.NET

mikel阅读(976)

[转载]Android 使用ContentProvider扫描手机中的图片,仿微信显示本地图片效果 – Mobile Internet developer – 博客频道 – CSDN.NET.

转载请注明本文出自xiaanming的博客(http://blog.csdn.net/xiaanming/article/details/18730223),请尊重他人的辛勤劳动成果,谢谢!

写 这篇文章之前,先简单说几句,首先是先恭喜下自己获得了2013年的博客之星称号,很意外也很开心,自己是从2013年开始写博客,那时候也不知道怎么 写,我从小就不喜欢写日记,作文什么的,所以刚开始都是贴代码,也没有人看,后面慢慢的,写的文章被推荐博客首页和CSDN首页(这里也要小小的感谢下小 编MM),访问量逐渐的多了起来,有更多的人看我的文章,这也使自己有了继续写文章的动力,也希望我写的东西对大家有点帮助吧,在2014年我会继续在 CSDN上面写博客,然后是感谢博客之星给我投票支持我的朋友们,谢谢你们支持我的每一票,最后就是2014春节马上就到了,提前祝福大家新年快乐,工作 顺利,事事顺心!

回到主题,之前群里面有朋友问我,有没有关于本地图片选择的Demo,类似微信的效果,他说网上没有这方面的Demo,问 我能不能写一篇关于这个效果的Demo,于是我研究了下微信的本地图片选择的Demo,自己仿照的写了下分享给大家,希望对以后有这样子需求的朋友有一点 帮助吧,主要使用的是ContentProvider扫描手机中的图片,并用GridView将图片显示出来,关于GridView和ListView显 示图片的问题,一直是一个很头疼的问题,因为我们手机的内存有限,手机给每个应用程序分配的内存也有限,所以图片多的情况下很容易伴随着OOM的发生,不 过现在也有很多的开源的图片显示框架,大家有兴趣的可以去了解了解,今天我的这篇文章使用的是LruCache这个类(之前写了一篇使用LruCache 加载网络图片的Android 异步加载图片,使用LruCache和SD卡或手机缓存,效果非常的流畅)以及对图片进行相对应的裁剪,这样也可以尽量的避免OOM的发生,我们先看下微信的效果吧

接下来我们就来实现这些效果吧,首先我们新建一个项目,取名ImageScan

首 先我们先看第一个界面吧,使用将手机中的图片扫描出来,然后根据图片的所在的文件夹将其分类出来,并显示所在文件夹里面的一张图片和文件夹中图片个数,我 们根据界面元素(文件夹名, 文件夹图片个数,文件夹中的一张图片)使用一个实体对象ImageBean来封装这三个属性

package com.example.imagescan;

/**
* GridView的每个item的数据对象
*
* @author len
*
*/
public class ImageBean{
/**
* 文件夹的第一张图片路径
*/
private String topImagePath;
/**
* 文件夹名
*/
private String folderName;
/**
* 文件夹中的图片数
*/
private int imageCounts;

public String getTopImagePath() {
return topImagePath;
}
public void setTopImagePath(String topImagePath) {
this.topImagePath = topImagePath;
}
public String getFolderName() {
return folderName;
}
public void setFolderName(String folderName) {
this.folderName = folderName;
}
public int getImageCounts() {
return imageCounts;
}
public void setImageCounts(int imageCounts) {
this.imageCounts = imageCounts;
}

}

接下来就是主界面的布局啦,上面的导航栏我没有加进去,只有下面的GridView,所以说主界面布局中只有一个GridView


接下来就是GridView的Item的布局,看上面的图也行你会认为他的效果是2张图片添加的效果,其实不是,后面的叠加效果只是一张背景图片而已,代码先贴上来

<!--?xml version="1.0" encoding="UTF-8"?-->

&nbsp;

&nbsp;

看到上面的布局代码,也行你已经发现了,上面使用的是自定义的MyImageView,我先不说这个自定义MyImageView的作用,待会再给大家说,我们继续看代码

第一个界面的主要代码

package com.example.imagescan;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.ContentResolver;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.MediaStore;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.GridView;
/**
* @blog http://blog.csdn.net/xiaanming
*
* @author xiaanming
*
*
*/
public class MainActivity extends Activity {
private HashMap&lt;String, List&gt; mGruopMap = new HashMap&lt;String, List&gt;();
private List list = new ArrayList();
private final static int SCAN_OK = 1;
private ProgressDialog mProgressDialog;
private GroupAdapter adapter;
private GridView mGroupGridView;

private Handler mHandler = new Handler(){

@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case SCAN_OK:
//关闭进度条
mProgressDialog.dismiss();

adapter = new GroupAdapter(MainActivity.this, list = subGroupOfImage(mGruopMap), mGroupGridView);
mGroupGridView.setAdapter(adapter);
break;
}
}

};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

mGroupGridView = (GridView) findViewById(R.id.main_grid);

getImages();

mGroupGridView.setOnItemClickListener(new OnItemClickListener() {

@Override
public void onItemClick(AdapterView&lt;?&gt; parent, View view,
int position, long id) {
List childList = mGruopMap.get(list.get(position).getFolderName());

Intent mIntent = new Intent(MainActivity.this, ShowImageActivity.class);
mIntent.putStringArrayListExtra("data", (ArrayList)childList);
startActivity(mIntent);

}
});

}

/**
* 利用ContentProvider扫描手机中的图片,此方法在运行在子线程中
*/
private void getImages() {
//显示进度条
mProgressDialog = ProgressDialog.show(this, null, "正在加载...");

new Thread(new Runnable() {

@Override
public void run() {
Uri mImageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
ContentResolver mContentResolver = MainActivity.this.getContentResolver();

//只查询jpeg和png的图片
Cursor mCursor = mContentResolver.query(mImageUri, null,
MediaStore.Images.Media.MIME_TYPE + "=? or "
+ MediaStore.Images.Media.MIME_TYPE + "=?",
new String[] { "image/jpeg", "image/png" }, MediaStore.Images.Media.DATE_MODIFIED);

if(mCursor == null){
return;
}

while (mCursor.moveToNext()) {
//获取图片的路径
String path = mCursor.getString(mCursor
.getColumnIndex(MediaStore.Images.Media.DATA));

//获取该图片的父路径名
String parentName = new File(path).getParentFile().getName();

//根据父路径名将图片放入到mGruopMap中
if (!mGruopMap.containsKey(parentName)) {
List chileList = new ArrayList();
chileList.add(path);
mGruopMap.put(parentName, chileList);
} else {
mGruopMap.get(parentName).add(path);
}
}

//通知Handler扫描图片完成
mHandler.sendEmptyMessage(SCAN_OK);
mCursor.close();
}
}).start();

}

/**
* 组装分组界面GridView的数据源,因为我们扫描手机的时候将图片信息放在HashMap中
* 所以需要遍历HashMap将数据组装成List
*
* @param mGruopMap
* @return
*/
private List subGroupOfImage(HashMap&lt;String, List&gt; mGruopMap){
if(mGruopMap.size() == 0){
return null;
}
List list = new ArrayList();

Iterator&lt;Map.Entry&lt;String, List&gt;&gt; it = mGruopMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry&lt;String, List&gt; entry = it.next();
ImageBean mImageBean = new ImageBean();
String key = entry.getKey();
List value = entry.getValue();

mImageBean.setFolderName(key);
mImageBean.setImageCounts(value.size());
mImageBean.setTopImagePath(value.get(0));//获取该组的第一张图片

list.add(mImageBean);
}

return list;

}

}

首先看getImages()这个方法,该方法是使用ContentProvider将手机中的图片扫描出来,我这里只扫描了手机的外部存储中的图片,由于手机中可能存在很多的图片,扫描图片又比较耗时,所以我们在这里开启了子线程去获取图片,扫描的图片都存放在Cursor中,我们先要将图片按照文件夹进行分类,我们使用了HashMap来进行分类并将结果存储到mGruopMap(Key是文件夹名,Value是文件夹中的图片路径的List)中,分类完了关闭Cursor并利用Handler来通知主线程
然后是subGroupOfImage()方法,改方法是将mGruopMap的数据组装到List中,在List中存放GridView中的每个item的数据对象ImageBean, 遍历HashMap对象,具体的逻辑看代码,之后就是给GridView设置Adapter。
设置item点击事件,点击文件夹跳转到展示文件夹图片的Activity, 我们需要传递每个文件夹中的图片的路径的集合

看GroupAdapter的代码之前,我们先看一个比较重要的类,本地图片加载器NativeImageLoader

package com.example.imagescan;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.os.Handler;
import android.os.Message;
import android.support.v4.util.LruCache;

/**
* 本地图片加载器,采用的是异步解析本地图片,单例模式利用getInstance()获取NativeImageLoader实例
* 调用loadNativeImage()方法加载本地图片,此类可作为一个加载本地图片的工具类
*
* @blog http://blog.csdn.net/xiaanming
*
* @author xiaanming
*
*/
public class NativeImageLoader {
private LruCache&lt;String, Bitmap&gt; mMemoryCache;
private static NativeImageLoader mInstance = new NativeImageLoader();
private ExecutorService mImageThreadPool = Executors.newFixedThreadPool(1);

private NativeImageLoader(){
//获取应用程序的最大内存
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);

//用最大内存的1/4来存储图片
final int cacheSize = maxMemory / 4;
mMemoryCache = new LruCache&lt;String, Bitmap&gt;(cacheSize) {

//获取每张图片的大小
@Override
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
}
};
}

/**
* 通过此方法来获取NativeImageLoader的实例
* @return
*/
public static NativeImageLoader getInstance(){
return mInstance;
}

/**
* 加载本地图片,对图片不进行裁剪
* @param path
* @param mCallBack
* @return
*/
public Bitmap loadNativeImage(final String path, final NativeImageCallBack mCallBack){
return this.loadNativeImage(path, null, mCallBack);
}

/**
* 此方法来加载本地图片,这里的mPoint是用来封装ImageView的宽和高,我们会根据ImageView控件的大小来裁剪Bitmap
* 如果你不想裁剪图片,调用loadNativeImage(final String path, final NativeImageCallBack mCallBack)来加载
* @param path
* @param mPoint
* @param mCallBack
* @return
*/
public Bitmap loadNativeImage(final String path, final Point mPoint, final NativeImageCallBack mCallBack){
//先获取内存中的Bitmap
Bitmap bitmap = getBitmapFromMemCache(path);

final Handler mHander = new Handler(){

@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
mCallBack.onImageLoader((Bitmap)msg.obj, path);
}

};

//若该Bitmap不在内存缓存中,则启用线程去加载本地的图片,并将Bitmap加入到mMemoryCache中
if(bitmap == null){
mImageThreadPool.execute(new Runnable() {

@Override
public void run() {
//先获取图片的缩略图
Bitmap mBitmap = decodeThumbBitmapForFile(path, mPoint == null ? 0: mPoint.x, mPoint == null ? 0: mPoint.y);
Message msg = mHander.obtainMessage();
msg.obj = mBitmap;
mHander.sendMessage(msg);

//将图片加入到内存缓存
addBitmapToMemoryCache(path, mBitmap);
}
});
}
return bitmap;

}

/**
* 往内存缓存中添加Bitmap
*
* @param key
* @param bitmap
*/
private void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null &amp;&amp; bitmap != null) {
mMemoryCache.put(key, bitmap);
}
}

/**
* 根据key来获取内存中的图片
* @param key
* @return
*/
private Bitmap getBitmapFromMemCache(String key) {
return mMemoryCache.get(key);
}

/**
* 根据View(主要是ImageView)的宽和高来获取图片的缩略图
* @param path
* @param viewWidth
* @param viewHeight
* @return
*/
private Bitmap decodeThumbBitmapForFile(String path, int viewWidth, int viewHeight){
BitmapFactory.Options options = new BitmapFactory.Options();
//设置为true,表示解析Bitmap对象,该对象不占内存
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
//设置缩放比例
options.inSampleSize = computeScale(options, viewWidth, viewHeight);

//设置为false,解析Bitmap对象加入到内存中
options.inJustDecodeBounds = false;

return BitmapFactory.decodeFile(path, options);
}

/**
* 根据View(主要是ImageView)的宽和高来计算Bitmap缩放比例。默认不缩放
* @param options
* @param width
* @param height
*/
private int computeScale(BitmapFactory.Options options, int viewWidth, int viewHeight){
int inSampleSize = 1;
if(viewWidth == 0 || viewWidth == 0){
return inSampleSize;
}
int bitmapWidth = options.outWidth;
int bitmapHeight = options.outHeight;

//假如Bitmap的宽度或高度大于我们设定图片的View的宽高,则计算缩放比例
if(bitmapWidth &gt; viewWidth || bitmapHeight &gt; viewWidth){
int widthScale = Math.round((float) bitmapWidth / (float) viewWidth);
int heightScale = Math.round((float) bitmapHeight / (float) viewWidth);

//为了保证图片不缩放变形,我们取宽高比例最小的那个
inSampleSize = widthScale &lt; heightScale ? widthScale : heightScale;
}
return inSampleSize;
}

/**
* 加载本地图片的回调接口
*
* @author xiaanming
*
*/
public interface NativeImageCallBack{
/**
* 当子线程加载完了本地的图片,将Bitmap和图片路径回调在此方法中
* @param bitmap
* @param path
*/
public void onImageLoader(Bitmap bitmap, String path);
}
}
  1. 该类是一个单例类,提供了本地图片加载,内存缓存,裁剪等逻辑,该类在加载本地图片的时候采用的是异步加载的方式,对于大图片的加载也是 比较耗时的,所以采用子线程的方式去加载,对于图片的缓存机制使用的是LruCache,使用手机分配给应用程序内存的1/4用来缓存图片,除了使用 LruCache缓存图片之外,还对图片进行了裁剪,举个很简单的例子,假如我们的控件大小是100 * 100, 而我们的图片是400*400,我们加载这么大的图片需要很多的内存,所以我们采用了图片裁剪,根据控件的大小来确定图片的裁剪比例,从而减小内存的消 耗,提过GridView滑动的流畅度,介绍里面几个比较重要的方法,computeScale()计算图片需要裁剪的比例,根据控件的大小和图片的大小 确定比例,如果图片比控件大,我们就进行裁剪,否则不需要。decodeThumbBitmapForFile()方法是根据计算好了图片裁剪的比例之后 从文件中加载图片,我们先设置options.inJustDecodeBounds = true表示解析不占用内存,但是我们能获取图片的具体大小,利用computeScale()计算好比例,在将 options.inJustDecodeBounds=false,再次解析Bitmap,这样子就对图片进行了裁剪。
  2. loadNativeImage(final String path, final Point mPoint, final NativeImageCallBack mCallBack)我们在客户端只需要调用该方法就能获取到Bitmap对象,里面的具体逻辑是先判断内存缓存LruCache中是否存在该 Bitmap,不存在就开启子线程去读取,为了方便管理加载本地图片线程,这里使用了线程池,池中只能容纳一个线程,读取完了本地图片先将Bitmap加 入到LruCache中,保存的Key为图片路径,然后再使用Handler通知主线程图片加载好了,之后将Bitmap和路径回调到方法 onImageLoader(Bitmap bitmap, String path)中,该方法的mPoint是用来封装控件的宽和高的对象,如果不对图片进行裁剪直接这个方法的重载方法 loadNativeImage(final String path, final NativeImageCallBack mCallBack)就行了,逻辑是一样的,只是这个方法不对图片进行裁剪

接下来就是GridView的Adapter类的代码

package com.example.imagescan;

import java.util.List;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.TextView;

import com.example.imagescan.MyImageView.OnMeasureListener;
import com.example.imagescan.NativeImageLoader.NativeImageCallBack;

public class GroupAdapter extends BaseAdapter{
private List list;
private Point mPoint = new Point(0, 0);//用来封装ImageView的宽和高的对象
private GridView mGridView;
protected LayoutInflater mInflater;

@Override
public int getCount() {
return list.size();
}

@Override
public Object getItem(int position) {
return list.get(position);
}

@Override
public long getItemId(int position) {
return position;
}

public GroupAdapter(Context context, List list, GridView mGridView){
this.list = list;
this.mGridView = mGridView;
mInflater = LayoutInflater.from(context);
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
final ViewHolder viewHolder;
ImageBean mImageBean = list.get(position);
String path = mImageBean.getTopImagePath();
if(convertView == null){
viewHolder = new ViewHolder();
convertView = mInflater.inflate(R.layout.grid_group_item, null);
viewHolder.mImageView = (MyImageView) convertView.findViewById(R.id.group_image);
viewHolder.mTextViewTitle = (TextView) convertView.findViewById(R.id.group_title);
viewHolder.mTextViewCounts = (TextView) convertView.findViewById(R.id.group_count);

//用来监听ImageView的宽和高
viewHolder.mImageView.setOnMeasureListener(new OnMeasureListener() {

@Override
public void onMeasureSize(int width, int height) {
mPoint.set(width, height);
}
});

convertView.setTag(viewHolder);
}else{
viewHolder = (ViewHolder) convertView.getTag();
viewHolder.mImageView.setImageResource(R.drawable.friends_sends_pictures_no);
}

viewHolder.mTextViewTitle.setText(mImageBean.getFolderName());
viewHolder.mTextViewCounts.setText(Integer.toString(mImageBean.getImageCounts()));
//给ImageView设置路径Tag,这是异步加载图片的小技巧
viewHolder.mImageView.setTag(path);

//利用NativeImageLoader类加载本地图片
Bitmap bitmap = NativeImageLoader.getInstance().loadNativeImage(path, mPoint, new NativeImageCallBack() {

@Override
public void onImageLoader(Bitmap bitmap, String path) {
ImageView mImageView = (ImageView) mGridView.findViewWithTag(path);
if(bitmap != null &amp;&amp; mImageView != null){
mImageView.setImageBitmap(bitmap);
}
}
});

if(bitmap != null){
viewHolder.mImageView.setImageBitmap(bitmap);
}else{
viewHolder.mImageView.setImageResource(R.drawable.friends_sends_pictures_no);
}

return convertView;
}

public static class ViewHolder{
public MyImageView mImageView;
public TextView mTextViewTitle;
public TextView mTextViewCounts;
}

}

首先我们将每个item的图片路径设置到该ImageView上面,然后利用NativeImageLoader来加载本地图片,很普通,但是我们想在 getView()中获取ImageView的宽和高存在问题,在getView()里面刚开始显示item的时候利用 ImageView.getWidth()获取的都是0,这个比较郁闷,但是我们想要获取ImageView的宽和高怎么办呢?于是我想到了自定义 ImageView,在onMeasure()中利用回调的模式主动通知 我ImageView测量的宽和高,但是这有一个小小的问题,就是显示GridView的第一个item的时候,获取的宽和高还是0,第二个就能正常获取 了,第一个宽和高为0,我们不对第一张图片进行裁剪而已,在效率上也没啥问题,不知道大家有没有好的方法,可以在getView()中获取Item中某个控件的宽和高。

自定义MyImageView的代码,我们只需要设置OnMeasureListener监听,当MyImageView测量完毕之后,就会将测量的宽和高回调到onMeasureSize()中,然后我们可以根据MyImageView的大小来裁剪图片

package com.example.imagescan;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.ImageView;

public class MyImageView extends ImageView {
private OnMeasureListener onMeasureListener;

public void setOnMeasureListener(OnMeasureListener onMeasureListener) {
this.onMeasureListener = onMeasureListener;
}

public MyImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}

public MyImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);

//将图片测量的大小回调到onMeasureSize()方法中
if(onMeasureListener != null){
onMeasureListener.onMeasureSize(getMeasuredWidth(), getMeasuredHeight());
}
}

public interface OnMeasureListener{
public void onMeasureSize(int width, int height);
}

}

上面这些代码就完成了第一个界面的功能了,接下来就是点击GridView的item跳转另一个界面来显示该文件夹下面的所有图片,功能跟第一个界面差不多,也是使用GridView来显示图片,第二个界面的布局代码我就不贴了,直接贴上界面的代码

package com.example.imagescan;

import java.util.List;

import android.app.Activity;
import android.os.Bundle;
import android.widget.GridView;
import android.widget.Toast;

public class ShowImageActivity extends Activity {
private GridView mGridView;
private List list;
private ChildAdapter adapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.show_image_activity);

mGridView = (GridView) findViewById(R.id.child_grid);
list = getIntent().getStringArrayListExtra("data");

adapter = new ChildAdapter(this, list, mGridView);
mGridView.setAdapter(adapter);

}

@Override
public void onBackPressed() {
Toast.makeText(this, "选中 " + adapter.getSelectItems().size() + " item", Toast.LENGTH_LONG).show();
super.onBackPressed();
}

}

GridView的item上面一个我们自定义的MyImageView用来显示图片,另外还有一个CheckBox来记录我们选中情况,Adapter的代码如下

package com.example.imagescan;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.GridView;

import com.example.imagescan.MyImageView.OnMeasureListener;
import com.example.imagescan.NativeImageLoader.NativeImageCallBack;
import com.nineoldandroids.animation.AnimatorSet;
import com.nineoldandroids.animation.ObjectAnimator;

public class ChildAdapter extends BaseAdapter {
private Point mPoint = new Point(0, 0);//用来封装ImageView的宽和高的对象
/**
* 用来存储图片的选中情况
*/
private HashMap&lt;Integer, Boolean&gt; mSelectMap = new HashMap&lt;Integer, Boolean&gt;();
private GridView mGridView;
private List list;
protected LayoutInflater mInflater;

public ChildAdapter(Context context, List list, GridView mGridView) {
this.list = list;
this.mGridView = mGridView;
mInflater = LayoutInflater.from(context);
}

@Override
public int getCount() {
return list.size();
}

@Override
public Object getItem(int position) {
return list.get(position);
}

@Override
public long getItemId(int position) {
return position;
}

@Override
public View getView(final int position, View convertView, ViewGroup parent) {
final ViewHolder viewHolder;
String path = list.get(position);

if(convertView == null){
convertView = mInflater.inflate(R.layout.grid_child_item, null);
viewHolder = new ViewHolder();
viewHolder.mImageView = (MyImageView) convertView.findViewById(R.id.child_image);
viewHolder.mCheckBox = (CheckBox) convertView.findViewById(R.id.child_checkbox);

//用来监听ImageView的宽和高
viewHolder.mImageView.setOnMeasureListener(new OnMeasureListener() {

@Override
public void onMeasureSize(int width, int height) {
mPoint.set(width, height);
}
});

convertView.setTag(viewHolder);
}else{
viewHolder = (ViewHolder) convertView.getTag();
viewHolder.mImageView.setImageResource(R.drawable.friends_sends_pictures_no);
}
viewHolder.mImageView.setTag(path);
viewHolder.mCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {

@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
//如果是未选中的CheckBox,则添加动画
if(!mSelectMap.containsKey(position) || !mSelectMap.get(position)){
addAnimation(viewHolder.mCheckBox);
}
mSelectMap.put(position, isChecked);
}
});

viewHolder.mCheckBox.setChecked(mSelectMap.containsKey(position) ? mSelectMap.get(position) : false);

//利用NativeImageLoader类加载本地图片
Bitmap bitmap = NativeImageLoader.getInstance().loadNativeImage(path, mPoint, new NativeImageCallBack() {

@Override
public void onImageLoader(Bitmap bitmap, String path) {
ImageView mImageView = (ImageView) mGridView.findViewWithTag(path);
if(bitmap != null &amp;&amp; mImageView != null){
mImageView.setImageBitmap(bitmap);
}
}
});

if(bitmap != null){
viewHolder.mImageView.setImageBitmap(bitmap);
}else{
viewHolder.mImageView.setImageResource(R.drawable.friends_sends_pictures_no);
}

return convertView;
}

/**
* 给CheckBox加点击动画,利用开源库nineoldandroids设置动画
* @param view
*/
private void addAnimation(View view){
float [] vaules = new float[]{0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.25f, 1.2f, 1.15f, 1.1f, 1.0f};
AnimatorSet set = new AnimatorSet();
set.playTogether(ObjectAnimator.ofFloat(view, "scaleX", vaules),
ObjectAnimator.ofFloat(view, "scaleY", vaules));
set.setDuration(150);
set.start();
}

/**
* 获取选中的Item的position
* @return
*/
public List getSelectItems(){
List list = new ArrayList();
for(Iterator&lt;Map.Entry&lt;Integer, Boolean&gt;&gt; it = mSelectMap.entrySet().iterator(); it.hasNext();){
Map.Entry&lt;Integer, Boolean&gt; entry = it.next();
if(entry.getValue()){
list.add(entry.getKey());
}
}

return list;
}

public static class ViewHolder{
public MyImageView mImageView;
public CheckBox mCheckBox;
}

}

第二个界面的Adapter跟第一个界面差不多,无非多了一个CheckBox用来记录图片选择情况,我们只需要对CheckBox设置 setOnCheckedChangeListener监听,微信的选中之后CheckBox有一个动画效果,所以我利用nineoldAndroids 动画库也给CheckBox加了一个动画效果,直接调用addAnimation()方法就能添加了,getSelectItems()方法就能获取我们 选中的item的position了,知道了选中的position,其他的信息就都知道了,微信有对图片进行预览的功能,我这里就不添加了,如果有这个 需求可以自行添加,给大家推荐一个https://github.com/chrisbanes/PhotoView

 

运行项目,效果如下

 

 

看 起来还不错吧,采用的是异步读取图片,对图片进行了缓存和裁剪,使得在显示本地图片方面比较流畅,GridView滑动也挺流畅的,也有效的避免OOM的 产生,工程中有些东西还没有贴完全,有兴趣的朋友可以下载Demo来运行一下,好了,今天的讲解到这里结束了,感谢大家观看,希望这篇有疑问的朋友可以在 下面留言,我会为大家解答的!

项目源码,点击下载

[转载].net处理JSON简明教程 - 小A的日志 - 网易博客

mikel阅读(1510)

[转载].net处理JSON简明教程 – 小A的日志 – 网易博客.

.net处理JSON简明教程

Json.Net是.net中的一种流行的高性能的JSON框架。

特点

  1. 灵活的JSON序列化转化.net对象为JSON字符串。和把JSON字符串转换为.net对象。
  2. 手动读写JSON的Linq to JSON
  3. 比.net内置的JSON序列化程序更高的性能和速度。
  4. 便于读写的JSON
  5. 从XML中读取或写入JSON
  6. 支持Silverlight和Windows phone.

    当你读写的JSON与.net类紧密关联时,JSON序列化程序是一个不错的选择。JSON序列化程序将自动读写相关类的JSON。

     

    如果你只对JSON里面的数据感兴趣、你没有与JSON相关联的.net类或者JSON数据与你的类没有任何关系,你需要从对象中手动读写数据。以上各种情况下,你可以使用LINQ To JSON. LINQ To JSON 允许你在.net中更容易的读取、写入、修改JSON数据。

     

    Download Json.NET from CodePlex or install using NuGet

    PM> Install-Package Newtonsoft.Json

     

    JSON.net下载地址列表

    http://json.codeplex.com/releases/view/74287

    提供JSON.NET 4.0 Release 3版本地址

    http://json.codeplex.com/releases/view/74287#DownloadId=287841

    更多JSON详细信息,请查看: http://james.newtonking.com/projects/json-net.aspx

     

     

    先构造一个简单的类employee

    Public class employee{

    Public string eid{get;set;}

    Public string ename{get;set;}

    Public string esex{get;set;}

    Public datetime birthday{get;set;}

    }

     

     

    JSON序列化与反序列化的方案

    1. 使用Json.Net 处理JSON序列化与反序列化

序列化方法

private string JsonSerialize(employee emp) {

return Newtonsoft.Json.JsonConvert.SerializeObject(emp);

}

反序列化方法

private employee JsonDeserialize(string json) {

return (employee)(Newtonsoft.Json.JsonConvert.DeserializeObject(json, typeof(employee)));

}

  1. 使用JavaScriptSerializer进行序列化、反序列化

    序列化方法

private string JsonSerialize (employee emp) {

return new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(emp);

}

反序列化

Private string JsonDeserialize (string json){

return (employee)(new System.Web.Script.Serialization.JavaScriptSerializer().Deserialize(json,typeof(employee)));

}

  1. 通过类DataContractJsonSerializer对JSON序列化和反序列化

    序列化方法

private string JsonSerialize (employee emp) {

        System.Runtime.Serialization.Json.DataContractJsonSerializer dcjs = new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(employee));

        System.IO.MemoryStream ms = new System.IO.MemoryStream();

        dcjs.WriteObject(ms, emp);

        string json = System.Text.Encoding.UTF8.GetString(ms.ToArray());

        ms.Close();

        return json;

         }

反序列化方法

private employee JsonDeserialize (string json) {

     System.Runtime.Serialization.Json.DataContractJsonSerializer dcjs = new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(employee));

    System.IO.MemoryStream ms = new System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes(json));

employee obj = (employee)(dcjs.ReadObject(ms));

return obj;

        }

  1. 通过Newtonsoft.Json.Linq.JObject获取JSON数据

private void LinqJson(string json) {

JObject obj = Newtonsoft.Json.Linq.JObject.Parse(json);

//获取全部数据值

foreach (JToken token in obj.Values()) {

Response.Write(token.ToString());

}

//获取唯一值

Response.Write(obj[“eid”].ToString()+“<br />”); }

  1. 5、LINQ to JSON Example

string json = @”{

  “”Name””: “”Apple””,

  “”Expiry””: new Date(1230422400000),

  “”Price””: 3.99,

  “”Sizes””: [

    “”Small””,

    “”Medium””,

    “”Large””

  ]

}”;

JObject o = JObject.Parse(json);

string name = (string)o[“Name”];

// Apple

JArray sizes = (JArray)o[“Sizes”];

string smallest = (string)sizes[0];

 

JSON序列化和反序列化日期时间的处理

JSON格式不直接支持日期和时间。DateTime值值显示为“/Date(700000+0500)/”形式的JSON字符串,其中第一个数字(在提供的示例中为 700000)是 GMT 时区中自 1970 1 1 日午夜以来按正常时间(非夏令时)经过的毫秒数。该数字可以是负数,以表示之前的时间。示例中包括“+0500”的部分可选,它指示该时间属于Local 类型,即它在反序列化时应转换为本地时区。如果没有该部分,则会将时间反序列化为Utc

使用DataContractJsonSerializer的序列化方式对日期格式进行处理,其他的()序列化方案的使用方式是相同的。设计思想是通过正则表达式匹配,替换JSON里面的日期数据为正常的日期格式。

序列化方法

private string JsonSerialize (employee emp) {

System.Runtime.Serialization.Json.DataContractJsonSerializer dcjs = new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(employee));

System.IO.MemoryStream ms = new System.IO.MemoryStream();

dcjs.WriteObject(ms, emp);

string json = System.Text.Encoding.UTF8.GetString(ms.ToArray());

ms.Close();

//替换JSON时间为字符串

string p = @”\\/Date\((\d+)\)\\/”;

System.Text.RegularExpressions.MatchEvaluator me = new System.Text.RegularExpressions.MatchEvaluator(ConvertJsonDateToDataString);

System.Text.RegularExpressions.Regex regex = new System.Text.RegularExpressions.Regex(p);

json = regex.Replace(json, me);

return json;

}

 

 

private string ConvertJsonDateToDataString(System.Text.RegularExpressions.Match m) {

string result = string.Empty;

DateTime dt = new DateTime(1970,1,1);

dt = dt.AddMilliseconds(long.Parse(m.Groups[1].Value));

dt = dt.ToLocalTime();

result = dt.ToString(“yyyy-MM-dd HH:mm:ss”);

return result;

}

 

反序列化方法

private employee JsonDeserialize (string json)

{

string p = @”\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}”;

System.Text.RegularExpressions.MatchEvaluator me = new System.Text.RegularExpressions.MatchEvaluator(ConvertDateStringToJsonDate);

System.Text.RegularExpressions.Regex reg = new System.Text.RegularExpressions.Regex(p);

json = reg.Replace(json, me);

 

System.Runtime.Serialization.Json.DataContractJsonSerializer dcjs=new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(employee));

System.IO.MemoryStream ms=new System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes(json));

employee emp=(employee)dcjs.ReadObject(ms);

return emp;

}

 

private string ConvertDateStringToJsonDate(System.Text.RegularExpressions.Match m)

{

string result = string.Empty;

DateTime dt = DateTime.Parse(m.Groups[0].Value);

dt = dt.ToUniversalTime();

TimeSpan ts = dt – DateTime.Parse(“1970-1-1”);

result = string.Format(“\\/Date({0}+0800)\\/”, ts.TotalMilliseconds);

return result;

}

[转载]android 拍照上传照片

mikel阅读(1096)

[转载]android 拍照上传照片-Android新手入门-eoe Android开发者社区_Android开发论坛 – Powered by Discuz!.

废话不多说,直接进入主题,想要在Android中实现拍照最简单饿方法就是New 一个 Intent 设置Action为Android.media.action.IMAGE_CAPTURE 然后使用startActivityForResult(intent,REQUEST_CODE)方法进入相机。当然还有很多方式可以实现,大家可以在 网上查找。但是要注意的是在进入相机前最好判断下sdcard是否可用,代码如下:

destoryBimap();

String state = Environment.getExternalStorageState();
if (state.equals(Environment.MEDIA_MOUNTED)) {
intent = new Intent("android.media.action.IMAGE_CAPTURE");
startActivityForResult(intent, REQUEST_CODE);
} else {
Toast.makeText(DefectManagerActivity.this,
R.string.common_msg_nosdcard, Toast.LENGTH_LONG).show();

}

当拍照完成以后需要在onActivityResult(int requestCode, int resultCode, Intent data)方法中获取拍摄的图片,android把拍摄的图片封装到bundle中传递回来,但是根据不同的机器获得相片的方式不太一样,所以会出现某一种方式获取图片为null的想象,解决办法就是做一个判断,当一种方式不能获取,就是用另一种方式,下面是分别获取相片的两种方式:

Uri uri = data.getData();

if (uri != null) {

photo = BitmapFactory.decodeFile(uri.getPath());
}

if (photo == null) {
Bundle bundle = data.getExtras();
if (bundle != null) {
photo = (Bitmap) bundle.get("data");

} else {

Toast.makeText(DefectManagerActivity.this,
getString(R.string.common_msg_get_photo_failure),
Toast.LENGTH_LONG).show();

return;

}

第一种方式是用方法中传回来的intent调用getData();方法获取数据的Uri,然后再根据uri获取数据的路径,然后根据路径封装成一个bitmap就行了.
第二种方式也是用法中传回来的intent对象但是不再是调用getData();方法而是调用getExtras();方法获取intent里面所有参数的一个对象集合bundle,然后是用bundle对象得到键为data的值也就是一个bitmap对象.

通过上面两种方式就能获取相片的bitmap对象,然后就可以在程序中是用,如果你想把相片保存到自己指定的目录可以是用如下步骤即可:
首先bitmap有个一compress(Bitmap.CompressFormat.JPEG, 100, baos)方法,这个方法有三个参数,第一个是指定将要保存的图片的格式,第二个是图片保存的质量,值是0-100,比如像PNG格式的图片这个参数你可以随便设置,因为PNG是无损的格式。第三个参数是你一个缓冲输出流ByteArrayOutputStream();,这个方法的作用就是把 bitmap的图片转换成jpge的格式放入输出流中,然后大家应该明白怎么操作了吧,下面是实例代码:

String pictureDir = "";

FileOutputStream fos = null;
BufferedOutputStream bos = null;

ByteArrayOutputStream baos = null;

try {
baos = new ByteArrayOutputStream();

bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);

byte[] byteArray = baos.toByteArray();
String saveDir = Environment.getExternalStorageDirectory()
+ "/temple";
File dir = new File(saveDir);

if (!dir.exists()) {

dir.mkdir();
}

File file = new File(saveDir, "temp.jpg");

file.delete();
if (!file.exists()) {
file.createNewFile();

}
fos = new FileOutputStream(file);
bos = new BufferedOutputStream(fos);
bos.write(byteArray);

pictureDir = file.getPath();

} catch (Exception e) {
e.printStackTrace();

} finally {
if (baos != null) {
try {

baos.close();
} catch (Exception e) {
e.printStackTrace();

}
}
if (bos != null) {
try {
bos.close();
} catch (Exception e) {
e.printStackTrace();
}

}

if (fos != null) {

try {

fos.close();

} catch (Exception e) {

e.printStackTrace();

}
}
}

然后就是实现图片的上传功能,我这里是是用的apache的HttpClient里面的MultipartEntity实现文件上传具体代码如下:

/**
* 提交参数里有文件的数据
*
* @param url
* 服务器地址
* @param param
* 参数
* <a href="\" target="\">@return</a> 服务器返回结果
* @throws Exception
*/
public static String uploadSubmit(String url, Map&lt;String, String&gt; param,
File file) throws Exception {
HttpPost post = new HttpPost(url);

MultipartEntity entity = new MultipartEntity();
if (param != null &amp;&amp; !param.isEmpty()) {
for (Map.Entry&lt;String, String&gt; entry : param.entrySet()) {
entity.addPart(entry.getKey(), new StringBody(entry.getValue()));
}
}
// 添加文件参数
if (file != null &amp;&amp; file.exists()) {
entity.addPart("file", new FileBody(file));
}
post.setEntity(entity);
HttpResponse response = httpClient.execute(post);
int stateCode = response.getStatusLine().getStatusCode();
StringBuffer sb = new StringBuffer();
if (stateCode == HttpStatus.SC_OK) {
HttpEntity result = response.getEntity();
if (result != null) {
InputStream is = result.getContent();
BufferedReader br = new BufferedReader(
new InputStreamReader(is));
String tempLine;
while ((tempLine = br.readLine()) != null) {
sb.append(tempLine);
}
}
}
post.abort();
return sb.toString();
}

这里就基本上对图片上传就差不多了,但是还有一个问题就是图片上传完以后bitmap还在内存中,而且大家都知道如果,高清的图片比较大,而手机内存本来就有限,如果不进行处理很容易报内存溢出,所以我们应该把处理完的bitmap从内存中释放掉,这时候就需要调用bitmap的recycle();方法,调用这个方法的时候需要注意不能太早也不能太晚,不然会报异常,一般可以放在下一张图片生成前或者没有任何view引用要销毁的图片的时候下面是实例代码:

/**
* 销毁图片文件
*/
private void destoryBimap() {
if (photo != null &amp;&amp; !photo.isRecycled()) {
photo.recycle();
photo = null;
}
}

源码下载地址:http://pan.baidu.com/s/1kT6vzuR

[转载]学习脚步--- HttpClient4.0 - - ITeye技术网站

mikel阅读(1064)

[转载]学习脚步— HttpClient4.0 – – ITeye技术网站.

HttpClient程序包是一个实现了 HTTP 协议的客户端编程工具包,要想熟练的掌握它,必须熟悉 HTTP协议。一个最简单的调用如下:

import java.io.IOException;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.DefaultHttpClient;

public class Test {
public static void main(String[] args) {

// 核心应用类
HttpClient httpClient = new DefaultHttpClient();

// HTTP请求
HttpUriRequest request =
new HttpGet("http://localhost/index.html");

// 打印请求信息
System.out.println(request.getRequestLine());
try {
// 发送请求,返回响应
HttpResponse response = httpClient.execute(request);

// 打印响应信息
System.out.println(response.getStatusLine());
} catch (ClientProtocolException e) {
// 协议错误
e.printStackTrace();
} catch (IOException e) {
// 网络异常
e.printStackTrace();
}
}
}

如果HTTP服务器正常并且存在相应的服务,则上例会打印出两行结果:

GET http://localhost/index.html HTTP/1.1
HTTP/1.1 200 OK

核心对象httpClient的调用非常直观,其execute方法传入一个request对象,返回一个response对象。使用httpClient发出HTTP请求时,系统可能抛出两种异常,分别是ClientProtocolException和IOException。第一种异常的发生通常是协议错误导致,如在构造HttpGet对象时传入的协议不对(例如不小心将”http”写成”htp”),或者服务器端返回的内容不符合HTTP协议要求等;第二种异常一般是由于网络原因引起的异常,如HTTP服务器未启动等。
从实际应用的角度看,HTTP协议由两大部分组成:HTTP请求和HTTP响应。那么HttpClient程序包是如何实现HTTP客户端应用的呢?实现过程中需要注意哪些问题呢?
HTTP请求

HTTP 1.1由以下几种请求组成:GET, HEAD, POST, PUT, DELETE, TRACE and OPTIONS, 程序包中分别用HttpGet, HttpHead, HttpPost, HttpPut, HttpDelete, HttpTrace, and HttpOptions 这几个类创建请求。所有的这些类均实现了HttpUriRequest接口,故可以作为execute的执行参数使用。
所有请求中最常用的是GET与POST两种请求,与创建GET请求的方法相同,可以用如下方法创建一个POST请求:

HttpUriRequest request = new HttpPost(
"http://localhost/index.html");

HTTP请求格式 告诉我们,有两个位置或者说两种方式可以为request提供参数:request-line方式与request-body方式。

request-line

request-line方式是指在请求行上通过URI直接提供参数。
(1)
我们可以在生成request对象时提供带参数的URI,如:

HttpUriRequest request = new HttpGet(
"http://localhost/index.html?param1=value1&amp;param2=value2");

(2)

另外,HttpClient程序包为我们提供了URIUtils工具类,可以通过它生成带参数的URI,如:

URI uri = URIUtils.createURI("http", "localhost", -1, "/index.html",
"param1=value1&amp;param2=value2", null);
HttpUriRequest request = new HttpGet(uri);
System.out.println(request.getURI());

上例的打印结果如下:
http://localhost/index.html?param1=value1&param2=value2
(3)
需要注意的是,如果参数中含有中文,需将参数进行URLEncoding处理,如:

String param = "param1=" + URLEncoder.encode("中国", "UTF-8") + "&amp;param2=value2";
URI uri = URIUtils.createURI("http", "localhost", 8080,
"/sshsky/index.html", param, null);
System.out.println(uri);

上例的打印结果如下:

http://localhost/index.html?param1=%E4%B8%AD%E5%9B%BD&param2=value2

request-body

与request-line方式不同,request-body方式是在request-body中提供参数,此方式只能用于POST请求。在HttpClient程序包中有两个类可以完成此项工作,它们分别是UrlEncodedFormEntity类与MultipartEntity类。这两个类均实现了HttpEntity接口。
(1)
使用最多的是UrlEncodedFormEntity类。通过该类创建的对象可以模拟传统的HTML表单传送POST请求中的参数。如下面的表单:


<form action="http://localhost/index.html" method="POST"><input type="text" name="param1" value="中国" />
<input type="text" name="param2" value="value2" />
</form>

我们可以用下面的代码实现:

List formParams = new ArrayList();
formParams.add(new BasicNameValuePair("param1", "中国"));
formParams.add(new BasicNameValuePair("param2", "value2"));
HttpEntity entity = new UrlEncodedFormEntity(formParams, "UTF-8");

HttpPost request = new HttpPost(“http://localhost/index.html”);
request.setEntity(entity);

当然,如果想查看HTTP数据格式,可以通过HttpEntity对象的各种方法取得。如:

List formParams = new ArrayList();
formParams.add(new BasicNameValuePair("param1", "中国"));
formParams.add(new BasicNameValuePair("param2", "value2"));
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formParams, "UTF-8");

System.out.println(entity.getContentType());
System.out.println(entity.getContentLength());
System.out.println(EntityUtils.getContentCharSet(entity));
System.out.println(EntityUtils.toString(entity));

上例的打印结果如下:

Content-Type: application/x-www-form-urlencoded; charset=UTF-8
39
UTF-8
param1=%E4%B8%AD%E5%9B%BD&amp;param2=value2

(2)
除了传统的application/x-www-form-urlencoded表单,我们另一个经常用到的是上传文件用的表单,这种表单的类型为multipart/form-data。在HttpClient程序扩展包(HttpMime)中专门有一个类与之对应,那就是MultipartEntity类。此类同样实现了HttpEntity接口。如下面的表单:


<form action="http://localhost/index.html" enctype="multipart/form-data" method="POST"><input type="text" name="param1" value="中国" />
<input type="text" name="param2" value="value2" />
<input type="file" name="param3" />
</form>

我们可以用下面的代码实现:

MultipartEntity entity = new MultipartEntity();
entity.addPart("param1", new StringBody("中国", Charset.forName("UTF-8")));
entity.addPart("param2", new StringBody("value2", Charset.forName("UTF-8")));
entity.addPart("param3", new FileBody(new File("C:\\1.txt")));

HttpPost request = new HttpPost(“http://localhost/index.html”);
request.setEntity(entity);

HTTP响应

HttpClient程序包对于HTTP响应的处理较之HTTP请求来说是简单多了,其过程同样使用了HttpEntity接口。我们可以从HttpEntity对象中取出数据流(InputStream),该数据流就是服务器返回的响应数据。需要注意的是,HttpClient程序包不负责解析数据流中的内容。如:

HttpUriRequest request = ...;
HttpResponse response = httpClient.execute(request);

// 从response中取出HttpEntity对象
HttpEntity entity = response.getEntity();

// 查看entity的各种指标
System.out.println(entity.getContentType());
System.out.println(entity.getContentLength());
System.out.println(EntityUtils.getContentCharSet(entity));

// 取出服务器返回的数据流
InputStream stream = entity.getContent();

// 以任意方式操作数据流stream
// 调用方式 略

附注:

本文说明的是HttpClient 4.0.1 ,该程序包(包括依赖的程序包)由以下几个JAR包组成:

commons-logging-1.1.1.jar
commons-codec-1.4.jar
httpcore-4.0.1.jar
httpclient-4.0.1.jar
apache-mime4j-0.6.jar
httpmime-4.0.1.jar

可以在此处 下载完整的JAR包。

[转载]HttpClient通过HttpPost传递参数上传图片代码 - 我们轻松了,并不是生活越来越容易了,而是我们越来越强 - ITeye技术网站

mikel阅读(1463)

[转载]HttpClient通过HttpPost传递参数 – 我们轻松了,并不是生活越来越容易了,而是我们越来越强 – ITeye技术网站.

HttpClient新版本(新老版本没有做明确界定,Sorry!)

package http;

import java.util.ArrayList;
import java.util.List;

import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;

public class HttpTests {

	/**
	 * @param args
	 * @throws Exception
	 */
	public static void main(String[] args) throws Exception {
		HttpClient httpclient = new DefaultHttpClient();
		HttpPost httpPost = new HttpPost("******/abc");
		List<NameValuePair> nvps = new ArrayList<NameValuePair>();
		nvps.add(new BasicNameValuePair("username", "vip"));
		nvps.add(new BasicNameValuePair("password", "secret"));
		httpPost.setEntity(new UrlEncodedFormEntity(nvps));
		httpclient.execute(httpPost);
		httpclient.getConnectionManager().shutdown();
	}

}

HttpClient 老版本:

package http;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.PostMethod;

public class HttpTests {

	/**
	 * @param args
	 * @throws Exception
	 */
	public static void main(String[] args) throws Exception {
		HttpClient httpclient = new HttpClient();
		PostMethod httpPost =new PostMethod("******/abc");
        NameValuePair[] param = { new NameValuePair("username", "vip")};
        httpPost.setRequestBody(param); 
		httpclient.executeMethod(httpPost);
	}

}

上传图片:

import java.io.File;
import java.io.IOException;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;

public class HttpClientPostMethod {
	
	public static void main(String[] args) throws ClientProtocolException, IOException {
		post();
	}

	public static void post() throws ClientProtocolException, IOException {
		HttpClient httpclient = new DefaultHttpClient();
		HttpPost post = new HttpPost("http://localhost:8080/action.jsp");
		FileBody fileBody = new FileBody(new File("/home/sendpix0.jpg"));
		StringBody stringBody = new StringBody("文件的描述");
		MultipartEntity entity = new MultipartEntity();
		entity.addPart("file", fileBody);
		entity.addPart("desc", stringBody);
		post.setEntity(entity);
		HttpResponse response = httpclient.execute(post);
		if(HttpStatus.SC_OK==response.getStatusLine().getStatusCode()){  
			
			HttpEntity entitys = response.getEntity();
			if (entity != null) {
				System.out.println(entity.getContentLength());
				System.out.println(EntityUtils.toString(entitys));
			}
        }
		httpclient.getConnectionManager().shutdown();
	}
}

[转载] java模拟form表单提交图片文件

mikel阅读(1067)

[转载]程序开发技术交流分享 » Post Topic » java模拟form表单提交图片文件.
这次要和别人开发的服务器端程序接口对接,之前实现的上传文件的方法对方接口无法接收。他们的接口需要类似这样的form提交数据:

<form enctype="multipart/form-data" action=http://url/path  method="POST">
<input name="filename” type="file" />
<input type="submit" value="test" />
</form> 

解决办法是引入commons-httpclient.jar,下载地址http://hc.apache.org/downloads.cgi,模拟form表单提交数据,代码如下:

    File f = new File("device1.png");
    PostMethod filePost = new PostMethod(
            "http://url/path");
    Part[] parts = { new FilePart("filename", f) };
    filePost.setRequestEntity(new MultipartRequestEntity(parts, filePost
            .getParams()));
    HttpClient clients = new HttpClient();

    int status = clients.executeMethod(filePost);
    try {
        BufferedReader rd = new BufferedReader(new InputStreamReader(
                filePost.getResponseBodyAsStream(), "UTF-8"));
        StringBuffer stringBuffer = new StringBuffer();
        String line;
        while ((line = rd.readLine()) != null) {
            stringBuffer .append(line);
        }
        rd.close();
        System.out.println("接受到的流是:" + stringBuffer + "—-" + status);
    } catch (Exception e) {
        throw new RuntimeException("error”,e);

    }

解释一下

filePost.setRequestEntity(new MultipartRequestEntity(parts, filePost
        .getParams())); 

设置多媒体参数,作用类似form表单中的enctype=”multipart/form-data” ,

Part[] parts = { new FilePart("filename", f) };

设定参数名称和值,类似form表单中的
try catch块主要是打印服务器端回传的数据。

[转载]CSS3+HTML5特效3 - 纵向无缝滚动 - z_gia - 博客园

mikel阅读(1046)

[转载]CSS3+HTML5特效3 – 纵向无缝滚动 – z_gia – 博客园.

老惯例,先看例子。

This is a test 1.
This is a test 2.
This is a test 3.
This is a test 4.
This is a test 5.
This is a test 1.

 

实现原理:

1. 利用CSS3的@keyframes规则创建动画效果;

2. 使用CSS3的animation效果完成滚动切换。

 

CSS代码

@-webkit-keyframes scrollText1 {
    0%{
        -webkit-transform: translateY(0px);
    }
    20%{
        -webkit-transform: translateY(-30px);
    }
    40%{
        -webkit-transform: translateY(-60px);
    }
    60%{
        -webkit-transform: translateY(-90px);
    }
    80%{
        -webkit-transform: translateY(-120px);
    }
    100%{
        -webkit-transform: translateY(-150px);
    }
}

@keyframes scrollText1 {
    0%{
        transform: translateY(0px);
    }
    20%{
        transform: translateY(-30px);
    }
    40%{
        transform: translateY(-60px);
    }
    60%{
        transform: translateY(-90px);
    }
    80%{
        transform: translateY(-120px);
    }
    100%{
        transform: translateY(-150px);
    }
}

.box3{
  position: relative;
  top: 20px;
  left: 20px;
  width: 200px;
  height: 30px;
  overflow: hidden;
  border:1px solid #ccc;
}

.border3{
  top: 0px;
  -webkit-animation:scrollText1 8s infinite  cubic-bezier(1,0,0.5,0) ;
  animation:scrollText1 8s infinite  cubic-bezier(1,0,0.5,0) ;
}

.border3 div{
  height: 30px;
}

.border3:hover{
  animation-play-state:paused;
  -webkit-animation-play-state:paused;
}

CSS代码说明:

@-webkit-keyframes及@keyframes定义了从0% ~ 100%之间,每过20%的时间,向上移动30px,总共有6次移动;
.box3 定义外容器的基本属性
.border3 定义了内容器的属性,-webkit-animation:scrollText1 8s infinite cubic-bezier(1,0,0.5,0) 和 animation:scrollText1 8s infinite cubic-bezier(1,0,0.5,0) 定义了用8s种循环一次,无限循环已经移动是的效果;
.border3 div 定义了纵向滚动内容的基本样式;
.border3:hover 定义了鼠标移入容器是的效果,animation-play-state:paused 及 -webkit-animation-play-state:paused 定义了动画暂停;

HTML代码

<div class="box3">
  <div class="border3">
    <div>This is a test 1.</div>
    <div>This is a test 2.</div>
    <div>This is a test 3.</div>
    <div>This is a test 4.</div>
    <div>This is a test 5.</div>
    <div>This is a test 1.</div>
  </div>
</div>

HTML代码说明:

定义了6条信息可以纵向滚动,其中前5条是真正纵向滚动的信息,第6条和第1条信息是一样的,原因很简单,因为使用了@keyframes方式来实现动画效果,第1条信息的效果是默认为停止的,所以用第6条信息制作一个替代方法,在第一次循环结束后,可以无缝继续滚动,大家可以去掉第6条试一下,就可以看见效果了。

至此,大功告成。

[转载]android开发中监控android软件网络请求的软件Charles使用入门 - 大侠(cer) - 博客园

mikel阅读(910)

[转载]android开发中监控android软件网络请求的软件Charles使用入门 – 大侠(cer) – 博客园.

1.下载并安状软件,官网在此:

http://www.charlesproxy.com/

2.前题条件,电脑和手机必须在同一网段

3.在Charles界面选择菜单

proxy->proxy settings

勾选”Enable transparent HTTP proxying”确定代理端口,默认”8888″就可以了

4.在手机的wifi节点上长按,然后在跳出的框选”修改网络”,如图:

5.然后勾选”显示高级选项”,在出现的内容填入代理主机的IP(也就是你装了Charles的机器),端口框填入刚才填的”8888″,保存如图:

 

6.这时Charles客户端会跳一个框问你什么,你通过就可以了