nginx安装配置flv流媒体服务器

apache也可以配置flv流媒体服务器,之前有文章介绍,但是用一些播放器播放的时候还是不能拖动,比如说使用VLC播放器播放的时候,拖动就不起作用,因此尝试使用nginx来配置,最后发现flowplay,或者VLC都可以完美播放并且可以任意拖动。

看看nginx的安装步奏

1.安装pcre

获取pcre编译安装包,在http://www.pcre.org/上可以获取当前最新的版本
解压缩pcre-xx.tar.gz包。
进入解压缩目录,执行./configure。
make & make install

2.安装openssl
获取openssl编译安装包,在http://www.openssl.org/source/上可以获取当前最新的版本。
解压缩openssl-xx.tar.gz包。
进入解压缩目录,执行./config。
make & make install

3.安装zlib
获取zlib编译安装包,在http://www.zlib.net/上可以获取当前最新的版本。
解压缩openssl-xx.tar.gz包。
进入解压缩目录,执行./configure。
make & make install

4.安装nginx
获取nginx,在http://nginx.org/en/download.html上可以获取当前最新的版本。
解压缩nginx-xx.tar.gz包。
进入解压缩目录,执行

./configure –with-pcre=/usr/harddisk/pcre-8.36 –with-http_flv_module –with-http_gzip_static_module –with-http_stub_status_module

make & make install

注意,可以不需要安装openssl , zlib可能操作系统就已经安装所以也不需要安装,但是pcre必须的,注意–with-pcre是指向源代码的目录而不是安装目录,不然的话编译报错。

5.配置虚拟路径

配置文件路径 vi /usr/local/nginx/conf/nginx.conf

location ^~/vedio/
        {
                alias /usr/harddisk/;
                flv;
                limit_rate 250k;
        }

以上就是配置路径/vedio/下的访问能使用flv模块并且限速,alias是对应的物理路径。

6.启动和停止

/usr/local/nginx/sbin/nginx
/usr/local/nginx/sbin/nginx -s stop

android实现拨打电话

调用系统自带的打电话功能非常简单,在应用中只需要调用对应的接口,并传入电话号码就可以了,界面会自动跳转到打电话界面,并且电话已经开始拨打,等待接通就可以了,下面就是示例的代码:

String number = "110";  
//用intent启动拨打电话  
Intent intent = new Intent(Intent.ACTION_CALL,Uri.parse("tel:"+number));  
startActivity(intent);

当然打电话是调用系统的activity,所以需要分配权限才能使用

AndroidManifest.xml中加入下面的权限即可

<!--添加可以向外拨打电话的权限  -->  
<uses-permission android:name="android.permission.CALL_PHONE"></uses-permission>

这就是拨打电话的全部,超级简单。

android中播放视频的几种方法

播放视频的方法有很多种,使用andorid自带的也可以播放大部分的视频,有些格式的视频需要专门的解码才能播放,大部分的视频网站都有自己的视频格式,同时自己提供播放软件,这就是最简单的防止盗版,同时也是根据需要压缩自己的视频文件可以减少存储空间,当然最重要的还是节省带宽;

在Android中,我们有三种方式来实现视频的播放:
1、使用其自带的播放器。指定Action为ACTION_VIEW,Data为Uri,Type为其MIME类型。
2、使用VideoView来播放。在布局文件中使用VideoView结合MediaController来实现对其控制。
3、使用MediaPlayer类和SurfaceView来实现,这种方式很灵活。

1.调用其自带的播放器:

Uri uri = Uri.parse(Environment.getExternalStorageDirectory().getPath()+"/Test_Movie.m4v");     
//调用系统自带的播放器    
Intent intent = new Intent(Intent.ACTION_VIEW);    
Log.v("URI:::::::::", uri.toString());    
intent.setDataAndType(uri, "video/mp4");    
startActivity(intent);

2.使用VideoView来实现:

Uri uri = Uri.parse(Environment.getExternalStorageDirectory().getPath()+"/Test_Movie.m4v");    
VideoView videoView = (VideoView)this.findViewById(R.id.video_view);    
videoView.setMediaController(new MediaController(this));    
videoView.setVideoURI(uri);    
videoView.start();    
videoView.requestFocus();

3.使用MediaPlayer:

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent"
    android:layout_height="fill_parent" android:weightSum="1">
    <SurfaceView android:layout_height="220dip" android:layout_gravity="center" android:id="@+id/surface" android:layout_weight="0.25" android:layout_width="320dip"></SurfaceView>
    <LinearLayout android:id="@+id/linearLayout1" android:layout_height="wrap_content" android:layout_width="fill_parent">
        <Button android:text="播放" android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
        <Button android:text="暂停" android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
        <Button android:text="停止" android:id="@+id/button3" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
    </LinearLayout>
</LinearLayout>

主程序:

public class SurfaceActivity extends Activity implements SurfaceHolder.Callback {
    /** Called when the activity is first created. */
    MediaPlayer player;
    SurfaceView surface;
    SurfaceHolder surfaceHolder;
    Button play,pause,stop;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        play=(Button)findViewById(R.id.button1);
        pause=(Button)findViewById(R.id.button2);
        stop=(Button)findViewById(R.id.button3);
        surface=(SurfaceView)findViewById(R.id.surface);
 
        surfaceHolder=surface.getHolder();  //SurfaceHolder是SurfaceView的控制接口
        surfaceHolder.addCallback(this);   //因为这个类实现了SurfaceHolder.Callback接口,所以回调参数直接this
        surfaceHolder.setFixedSize(320, 220);  //显示的分辨率,不设置为视频默认
        surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);  //Surface类型
 
        play.setOnClickListener(new OnClickListener(){
             @Override
            public void onClick(View v) {
                player.start();
            }});
        pause.setOnClickListener(new OnClickListener(){
            @Override
            public void onClick(View v) {
                player.pause();
            }});
        stop.setOnClickListener(new OnClickListener(){
             @Override
            public void onClick(View v) {
                player.stop();
            }});
    }
 
    @Override
    public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
    }
 
    @Override
    public void surfaceCreated(SurfaceHolder arg0) {
//必须在surface创建后才能初始化MediaPlayer,否则不会显示图像
        player=new MediaPlayer();
        player.setAudioStreamType(AudioManager.STREAM_MUSIC);
        player.setDisplay(surfaceHolder);
        //设置显示视频显示在SurfaceView上
            try {
                player.setDataSource("/sdcard/3.mp4");
                player.prepare();
            } catch (Exception e) {
                e.printStackTrace();
            }
    }
 
    @Override
    public void surfaceDestroyed(SurfaceHolder arg0) {
        // TODO Auto-generated method stub
 
    }
 
    @Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
        if(player.isPlaying()){
        player.stop();
        }
        player.release();
        //Activity销毁时停止播放,释放资源。不做这个操作,即使退出还是能听到视频播放的声音
    }
}

总结

第三种算是比较常用的播放方式,比较灵活可以对视频进行控制,当然这毕竟是自带的播放器,支持视频格式根据系统安装的情况而定,那么可以使用一些第三方播放器来实现,比如说vlc开源播放器http://www.videolan.org/,或者其他的开源播放器都可以做的很好。

android使用ACTION_SEND分享内容

几乎是所有的app现在都有分享的功能,分享一些文本,链接和图片之类的到各大社交平台,那么这么一个简单的功能是如何实现的能,android里面有一个默认的接口,我们来看看怎么使用。

android默认的intent ACTION_SEND可以实现这个分享的功能,看看下面的示例

Intent sharingIntent = new Intent(Intent.ACTION_SEND);
sharingIntent.setType("text/html");
sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, Html.fromHtml("<p>This is the text that will be shared.</p>"));
startActivity(Intent.createChooser(sharingIntent,"Share using"));

以上是分享一段html,也是最常用的分享方式,也可以分享一些图片如下

Intent sharingIntent = new Intent(Intent.ACTION_SEND);
Uri screenshotUri = Uri.parse(path);

sharingIntent.setType("image/png");
sharingIntent.putExtra(Intent.EXTRA_STREAM, screenshotUri);
startActivity(Intent.createChooser(sharingIntent, "Share image using"));

分享多个图片

ArrayList<Uri> imageUris = new ArrayList<Uri>();
imageUris.add(imageUri1); // Add your image URIs here
imageUris.add(imageUri2);

Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND_MULTIPLE);
shareIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, imageUris);
shareIntent.setType("image/*");
startActivity(Intent.createChooser(shareIntent, "Share images to.."));

上面的分享当然是在手机上有可以分享的软件才能接受到分享的内容,以上的分享事件都会打开一个选择分享软件的列表,比如微信或者微博之类的,用户选择一个之后发送分享内容,那么下面这段xml可以让你的app可以出现在分享列表中,以实现分享的效果:

<activity android:name=".ui.MyActivity" >
    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="image/*" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="text/plain" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.SEND_MULTIPLE" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="image/*" />
    </intent-filter>
</activity>

下面这段代码是处理接收到消息之后的基本过程

void onCreate (Bundle savedInstanceState) {
    ...
    // Get intent, action and MIME type
    Intent intent = getIntent();
    String action = intent.getAction();
    String type = intent.getType();

    if (Intent.ACTION_SEND.equals(action) && type != null) {
        if ("text/plain".equals(type)) {
            handleSendText(intent); // Handle text being sent
        } else if (type.startsWith("image/")) {
            handleSendImage(intent); // Handle single image being sent
        }
    } else if (Intent.ACTION_SEND_MULTIPLE.equals(action) && type != null) {
        if (type.startsWith("image/")) {
            handleSendMultipleImages(intent); // Handle multiple images being sent
        }
    } else {
        // Handle other intents, such as being started from the home screen
    }
    ...
}

void handleSendText(Intent intent) {
    String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
    if (sharedText != null) {
        // Update UI to reflect text being shared
    }
}

void handleSendImage(Intent intent) {
    Uri imageUri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
    if (imageUri != null) {
        // Update UI to reflect image being shared
    }
}

void handleSendMultipleImages(Intent intent) {
    ArrayList<Uri> imageUris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
    if (imageUris != null) {
        // Update UI to reflect multiple images being shared
    }
}

总结

android自带的分享只是列出接收分享内容的app列表,由用户选择启动对应的app进行分享,当系统中没有安装目标app的时候是没办法分享的,那么对一般的应用来说这就已经足够了,系统自带的功能一般都是比较基本的东西,另一个办法是使用第三方分享sdk可以实现更加复杂的分享功能。

c++使用CreateProcess启动程序

有时候需要在程序中启动第三方程序,执行某个命令行之类的事情,也可以做个简单的启动程序启动浏览器之类的传入一些参数,下面来看个简单的例子:

创建一个命令行程序,启动程序的时候可能会遇到一些权限问题,在项目的属性里面可以设置启动的权限, 使用管理员权限

image

下面是个启动浏览器的简单示例

#include "stdafx.h"
#include <windows.h>
#include <string>
#include<stdio.h>

#pragma comment( linker, "/subsystem:windows /entry:mainCRTStartup" )
int main(int argc, _TCHAR* argv[])
{
	TCHAR cmdline[] = L"C:\\Program Files\\Internet Explorer\\iexplore.exe http://www.fullstacks.cn";
	STARTUPINFO si;
	memset(&si, 0, sizeof(STARTUPINFO));  
	si.cb = sizeof(STARTUPINFO);  
	si.dwFlags = STARTF_USESHOWWINDOW;  
	si.wShowWindow = SW_SHOW;  
	PROCESS_INFORMATION pi;
	CreateProcess(NULL,cmdline,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi);

	return 0;
}

其中#pragma comment …这一行的意思是启动的时候不显示命令行窗口,双击打开目标程序的时候直接就打开浏览器了;

对于启动其他程序或者启动java程序等可能需要设置一些环境变量或者获取一些环境变量

可以通过方法GetEnvironmentVariableA 获取环境变量,方法SetEnvironmentVariableA设置环境变量

char userdir[256];
memset(userdir,0,buffSize);
//获取环境变量APPDATA的值
GetEnvironmentVariableA("APPDATA", userdir, 256);
//设置环境变量
SetEnvironmentVariableA("GOOGLE_API_KEY"," ");
SetEnvironmentVariableA("GOOGLE_DEFAULT_CLIENT_ID"," ");
SetEnvironmentVariableA("GOOGLE_DEFAULT_CLIENT_SECRET"," ");

将char转换为tchar

//转换为tchar
TCHAR lastcmd[256];
memset(lastcmd,0,256);
MultiByteToWideChar(CP_ACP, 0, userdir, -1, lastcmd, strlen(userdir));

总结

做个启动快捷方式的时候如果使用系统的快捷方式,有时候会被一些软件修改掉,比如说启动浏览器并打开某个指定网站的启动快捷方式,很多应用系统需要这样的快捷方式放在桌面上,以便用户进入系统,但是往往会被一些流氓软件轻轻松松改掉打开一些广告网站,这样就影响很不好了,那么如果使用上面这种方式写一个exe启动你的浏览器然后以你的系统作为默认打开的状态,那么这个体验就非常不错而且不用担心被流氓软件修改你的启动程序。

java导出可执行jar包与启动脚本

java写桌面程序的时候如何启动有很多种办法,有些打包工具就可以直接生成一个快捷方式启动可执行的jar包,也可以直接写一个启动脚本启动,我们来看看启动脚本是怎么写的。

1.导出可执行jar包,首先需要创建一个运行配置

image

2.起个名字并选择需要导出的项目以及启动的时候的入口函数main所在的类

image

3.在项目上右键->导出,选择可执行jar包

image

4.选择上面配置好的运行配置,选择导出的路径

image

这样就导出成功了,依赖的jar包会导到一个文件夹里

5.写个脚本,并制定虚拟机路径

示例脚本如下

@cls
set JRE_HOME=%cd%\jre6
set JAVA_HOME=%cd%\jre6
PATH=%JRE_HOME%\bin;%PATH%

set CLASSPATH = %JRE_HOME%\lib\rt.jar;

@echo %JRE_HOME%
start javaw -jar myapp.jar

把依赖的jar包放到可执行jar包同级的lib目录里面就可以了。

maven依赖war包创建项目

对于bs结构的项目,一般公司都是会有一个适合自己的通用框架,项目在此基础上添加业务功能,那么基础框架可以发布成一个独立的war包,其他项目都依赖与这个包来创建,当版本更新的时候就可以很方便的升级基础框架的东西。

下面来看一下创建的过程

1.创建maven项目

image

2.选择简单项目

image

3.输入项目的信息,和依赖的war包的信息

image

这样项目就创建完成了

打开创建好之后的项目里的文件pom.xml

可以看到依赖项如下

  <parent>
    <groupId>fullstacks</groupId>
    <artifactId>fullstacks.parent</artifactId>
    <version>1.0.0</version>
  </parent>

这样如果war包没有问题的话,就可以运行起来了,当然先配好tomcat

4.编译

image

第一次编译的时候会下载war包的内容,并生成目标文件

在项目的根目录下生成一个目录叫target,里面有下载的war包以及和新项目加入的文件,如果新项目里面有相同路径的文件则会覆盖掉依赖的内容,这样就形成了新的项目。如果想覆盖掉基础框架的文件比如js,html和资源文件之类的,只需要在相同的路径建立相同的文件就可以了。

c++创建atl activex的简单步骤

创建activex可以基于mcf框架也可以基于atl创建,如果是创建没有界面的activex控件,atl更加小巧,当然atl也可以支持界面的操作,但是比较简陋可以安装wtl进行扩展。下面来看看一个简单的atl创建控件的简单示例

1.创建项目,勾选下面的选项,其他默认

image

2.打开类视图,添加一个类

image

3.添加一个简单对象,给个类名就可以了

image

4.勾选下面的选项,其他默认

image

5.在类图里面找到接口,在接口上添加方法

image

6.添加输入参数输出参数,注意输出参数必须是指针类型,要不然选不了out类型

image

7.找源文件Add.cpp,看到新添加的方法实现,写上方法的实现逻辑

STDMETHODIMP CAdd::add(LONG a, LONG b, LONG* c)
{

	*c = a + b;

	return S_OK;
}

这样整个过程就完成了,编译项目这个空间就会自动注册了,也可以通过命令行注册

regsvr32 demo.dll

8.注册之后的classid可以从扩展名为idl的文件中找到,如下

image

9.在页面上可以如下使用

<object classid="clsid:8FA96F06-568C-40E3-8F51-69E77D984D9E" id="myAddObj" name="myAddObj"/>
<script>
	var result = myAddObj.add(1,2);
	alert(result);
</script>

总结

atl创建的控件最后打包的是dll文件,而mfc貌似是扩展名为ocx的文件,其实本质都是dll,通过注册可以在页面上根据classid来引用,那么编译完成之后的dll可以打包成cab文件在页面上使用,前面文章有介绍如何打包并签名cab文件。

c++对图片进行base64编码

有时候我们需要把图片编码成base64编码的文本,然后保存起来,这时候就需要把图片的二进制数据读到内存,然后按照base64的算法进行编码得到一大串文本,这样就方便存放或者在网络上传输,当然也可以在网页上显示。

下面是一个完整的c++实现的base64编码解码程序代码

base64.h

#include "stdafx.h"
#include <string>

std::string base64_encode(unsigned char const* , unsigned int len);
std::string base64_decode(std::string const& s);

base64.cpp

#include "base64.h"
#include <iostream>
using namespace std;

static const std::string base64_chars = 
             "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
             "abcdefghijklmnopqrstuvwxyz"
             "0123456789+/";

static inline bool is_base64(unsigned char c) {
  return (isalnum(c) || (c == '+') || (c == '/'));
}

/**
* 对二进制进行编码
*/
std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {

  std::string ret;
  int i = 0;
  int j = 0;
  unsigned char char_array_3[3];
  unsigned char char_array_4[4];

  while (in_len--) {
    char_array_3[i++] = *(bytes_to_encode++);
    if (i == 3) {
      char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
      char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
      char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
      char_array_4[3] = char_array_3[2] & 0x3f;

      for(i = 0; (i <4) ; i++)
        ret += base64_chars[char_array_4[i]];
      i = 0;
    }
  }

  if (i)
  {
    for(j = i; j < 3; j++)
      char_array_3[j] = '\0';

    char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
    char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
    char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
    char_array_4[3] = char_array_3[2] & 0x3f;

    for (j = 0; (j < i + 1); j++)
      ret += base64_chars[char_array_4[j]];

    while((i++ < 3))
      ret += '=';

  }

  return ret;

}

/**
* 对base64串进行解码
*/
std::string base64_decode(std::string const& encoded_string) {
  int in_len = encoded_string.size();
  int i = 0;
  int j = 0;
  int in_ = 0;
  unsigned char char_array_4[4], char_array_3[3];
  std::string ret;

  while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
    char_array_4[i++] = encoded_string[in_]; in_++;
    if (i ==4) {
      for (i = 0; i <4; i++)
        char_array_4[i] = base64_chars.find(char_array_4[i]);

      char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
      char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
      char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

      for (i = 0; (i < 3); i++)
        ret += char_array_3[i];
      i = 0;
    }
  }

  if (i) {
    for (j = i; j <4; j++)
      char_array_4[j] = 0;

    for (j = 0; j <4; j++)
      char_array_4[j] = base64_chars.find(char_array_4[j]);

    char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
    char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
    char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

    for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
  }

  return ret;
}

base64编码后会得到类似这样的一串文本:“/9j/4AAQSkZJRgABAQAAAQABA…”

在网页上可以直接显示base64编码的图片,需要在前面加上前缀:“data:image/jpg;base64,”

下面是显示图片的例子

<img id="jpg" src="data:image/jpg;base64,/9j/4AAQSkZJRgABAQAAAQABA..."/>

总结

图片不一定非要保存成文件存放在服务器上,也不一定非得保持二进制格式存放,也可以变成base64编码的字符串存放在数据库里面,比如说读取的身份证头像数据完全可以转换成base64编码的字符串存在数据库里,显示的时候直接能够显示不管是在网页上或者在文档里面都是可以的。

windows下cab打包与签名

cab的打包有各式各样的工具,windows下一直有个默认的工具iexpress可以做到这一点,下面简单看一下一个简单的示例如何打包一个cab并在网页上引用

1、制作inf文件

default.INF
最开始一般是[Version]区:

[Version]
signature=” $CHICAGO$ ”
AdvancedINF=2.0
接下来就是最重要的[Add.Code]区:

[Add.Code]
getcertocx.ocx=getcertocx.ocx
前面是要下载的文件名,后面是对应这个文件的区域名,可以是任何名字,不过一般都是和文件的名字相同,这样方便维护。
再接下来是各个文件的区域了
[getcertocx.ocx]
file-win32-x86=thiscab
RegisterServer=yes
clsid={0A0488CF-F9AB-4AC4-AD8F-CD893553DD22}
DestDir=11
FileVersion=1,0,0,1

[getcertocx.ocx]区域中的第一个file值告诉ie到哪里去得到这个ocx,file一共包括三个部分,第一部分是file,这个永远都是这样的;第二部分告诉声明支持的OS,win32表示windows,mac就是苹果MAC OX了;第三部分是CPU类型,比如说x86、mips等

file的值可以取三个:一个URL、ignore和thiscab,如果是URL则说明到URL所在的位置去下;如果是ignore说明对于这种OS和CPU,不需要下载这个文件;如果是thiscab就在当前的cab文件中了。

第二部分是RegisterServer,可以取两个值yes和no,如果为yes则说明ie要注册该ocx,如果是no就不必了;

接下来是clsid,需要填写该ocx的class guid。

再下来是DestDir,它的值是ocx将要存到本地硬盘的位置,如果它的值是10,则将ocx放到\Windows或者\WinNT下;如果是11,则放到\Windows\System或者

\WinNT\System32下;如果是空(就是没有值)则会放到\Windows或者\WinNT下的Downloaded Program Files目录下;

最后是FileVersion,说明了ocx的版本号。

完整的示例default.INF文件如下

[Version] 
signature="$CHICAGO$" 
AdvancedINF=2.0 

[Add.Code] 
getcertocx.ocx=getcertocx.ocx 

[getcertocx.ocx] 
file-win32-x86=thiscab 
RegisterServer=yes 
clsid={0A0488CF-F9AB-4AC4-AD8F-CD893553DD22} 
DestDir=11 
FileVersion=1,0,0,1 

2、制作cab文件

利用iexpress.exe(windows提供的一个向导式cab制作工具) 在路径C:\Windows\System32\iexpress.exe

1

选“创建新的自解压缩指定文件”,点“下一步”。

2

选“仅创建压缩文件(ActiveX安装)”,点“下一步”。

3

点“添加”将上边制作好的default.inf和getcertocx.ocx文件添加进来,点“下一步”。

4

点“浏览”输入要生成的CAB文件名称,并选中“在软件包中使用长文件名保存文件”,之后点“下一步”。

5

选“不保存”,点“下一步”。

6

点“下一步”。

8

点“完成”退出向导,cab文件生成。

9

3.web上引用

<object width="0" HEIGHT="0"
 classid="CLSID:0A0488CF-F9AB-4AC4-AD8F-CD893553DD22"
 codebase="getcert.cab#Version=1,0,0,1">
</object>

注意classid和版本号要与inf文件中的一样,而且版本号是dll或者ocx文件本身的真实版本,这样的话后续版本修改之后网页可自动更新。

4.对cab文件进行签名

一般来说如果做一个自签名的证书其实意义不是很大,只有正版的证书才有意义

这里是制作一个自签名的证书并对cab文件进行签名,使用的是Visual Studio2010的命令行工具

image

执行下面四行命令可对文件进行签名

makecert -r -sv fullstacks_key.pvk -n "CN=fullstacks" fullstacks.cer
cert2spc fullstacks.cer fullstacks.spc
pvk2pfx -pvk fullstacks_key.pvk -pi fullstacks -spc fullstacks.spc -pfx fullstacks.pfx -po fullstacks
signtool  sign  /f fullstacks.pfx /p fullstacks  *.cab

5.在cab安装的时候执行exe

下面是一个示例每次都执行的

[Setup Hooks]
hook1=hook1

[hook1]
run=%EXTRACT_DIR%\example_setup.exe /q

[Version]
; This section is required for compatibility on both Windows 95 and Windows NT.
Signature="$CHICAGO$"
AdvancedInf=2.0

下面是一个有条件执行,当机器里没有注册指定的dll的时候运行,当然也可以加上文件版本,如果版本更新也可以执行

[Add.Code]
example.ocx=example.ocx

[example.ocx]
Clsid={...}
hook=hook1
FileVersion=1,0,0,1

[hook1]
run=%EXTRACT_DIR%\example_setup.exe /q

[Version]
; This section is required for compatibility on both Windows 95 and Windows NT.
Signature="$CHICAGO$"
AdvancedInf=2.0

总结

cab在网页使用的场景比较多,最大的好处就是能够自动下载安装,如果浏览器里面当前网页在新人区里的话可以自动下载安装没有任何提示,这个在用户体验上也比较好,可以默默无闻的进行更新,这种情况比较适合计算机水平比较低的最终用户,不需要操作就可以使用最新的功能,但是对一些高级用户就不一合适。不过这些只是比较合适IE的用户,其他浏览器或多或少的总是有一些不一样的地方。

windows下编译使用libjpeg

libjpeg是一个图片处理c语言实现,可以比较方便的将几种图片格式互相转换,是个开放源代码软件,需要下载源代码编译之后才能使用,下载地址http://www.infai.org/jpeg/,windows下下载jpegsr9a.zip文件,使用Visual Studio 2010编译工具进行编译。

1.使用Visual Studio2010编译

首先将文件解压到某个目录,将文件jconfig.vc改为jconfig.h

然后打开Visual Studio2010的命令行工具

image

在解压目录下输入编译命令

nmake /f makefile.vc nodebug=1

这样就编译完成了,目录下会生成libjpeg.lib文件,还有其他一些工具如cjpeg.exe

可以使用cjpeg.exe通过命令行直接转换文件如

cjpeg.exe a.bmp b.jpg

可以直接将a.bmp文件转换为b.jpg文件。

2.使用libjpeg.lib

将这四个文件libjpeg.lib,jconfig.h,jmorecfg.h,jpeglib.h复制得到自己的项目中

在项目属性里面引入libjpeg.ib

image

在项目中需要用的的地方引入头文件

extern "C" {
	#include "jpeglib.h"
	#include "jmorecfg.h"
	#include "jconfig.h"
}

这样就可以调用库函数了

3.c与c++混合使用

Visual Studio2010里面混合使用c与c++需要配置不要使用预编译头文件

image

4.bmp转换为jpg示例

libjpeg.lib里面并没与读取bmp的辅助方法,但是cjpeg.exe 工具有,使用到以下几个文件

rdbmp.c,cdjpeg.c,cdjpeg.h,jinclude.h,jerror.h

若果是自己实现读取bmp文件的话就不需要添加上面的5个文件,这些文件可以独立放在一个目录里面再在项目中引用即可;

下面是一个完整的示例,使用Visual Studio2010创建一个c++命令行程序,这里是c和c++混合使用,所以需要配置不使用预编译头才能编译通过,使用下面的代码即可编译运行

#include "stdafx.h"
#include <stdio.h>

extern "C" {  
	#include "jpeglib.h"  
	#include "jmorecfg.h"  
	#include "jconfig.h"  
	#include "cdjpeg.h"
}

/**
* bmp 文件转换为jpg文件
*/
int bmp2jpg(char * input_file_name,char * output_file_name){
	//输入输出文件
	FILE * input_file;
	FILE * output_file;

	//图片转换相关数据结构
	cjpeg_source_ptr src_mgr;
	struct jpeg_compress_struct cinfo;
	struct jpeg_error_mgr jerr;
	JDIMENSION num_scanlines;

	//初始化
	cinfo.err = jpeg_std_error(&jerr);
	jpeg_create_compress(&cinfo);

	cinfo.in_color_space = JCS_RGB; /* arbitrary guess */
	jpeg_set_defaults(&cinfo);

	//读取源文件
	input_file = fopen(input_file_name,"rb");  
	if(input_file==0) return 0; 
	src_mgr =jinit_read_bmp(&cinfo);

	src_mgr->input_file = input_file;
	(*src_mgr->start_input) (&cinfo, src_mgr);
	jpeg_default_colorspace(&cinfo);
	
	//创面目标文件
	output_file = fopen(output_file_name,"wb");
	if(output_file==0) return 0;
	jpeg_stdio_dest(&cinfo, output_file);

	/* Start compressor */
	jpeg_start_compress(&cinfo, TRUE);

	/* Process data */
	while (cinfo.next_scanline < cinfo.image_height) {
		num_scanlines = (*src_mgr->get_pixel_rows) (&cinfo, src_mgr);
		(void) jpeg_write_scanlines(&cinfo, src_mgr->buffer, num_scanlines);
	}

	/* Finish compression and release memory */
	(*src_mgr->finish_input) (&cinfo, src_mgr);
	jpeg_finish_compress(&cinfo);
	jpeg_destroy_compress(&cinfo);

	fclose(input_file);
	fclose(output_file);

	return 1;
}


int _tmain(int argc, _TCHAR* argv[])
{
	char input_file_name[128]  = "c:\\a.bmp";
	char output_file_name[128] = "c:\\b.jpg";

	bmp2jpg(input_file_name,output_file_name);
}

这里可以手工截屏使用画图工具保存为a.bmp文件,然后执行上面的程序就看到转换之后的文件了。

总结

c语言实现的转换效率还是非常之高的,可以写成公共库让其他平台的语言调用,只需要传入文件名即可,非常的方便,在linux下的话需要重新编译。

linux下apache ftpserver的安装配置

apache-ftpd安装过程非常简单,但是需要做一些配置,一个是用户的配置另外一个就是端口的配置,在防火墙iptables打开的情况需要配置两个端口一个用与ftp连接一个用于ftp数据的传输。

从官方网站下载http://mina.apache.org/ftpserver-project/downloads.html ,下载linux包ftpserver-1.0.6.tar.gz

1.安装文件

比如安装到路径/usr/local/apache-ftpd

tar zxvf ftpserver-1.0.6.tar.gz
cp ftpserver-1.0.6 /usr/local/apache-ftpd

2.配置用户

编辑文件res/conf/user.properties

vi res/conf/user.properties

配置用户admin并设置ftp保存的文件路径,注意对目录的可写权限是writepermission=true,要不然不能上传文件

# Password is "admin"
ftpserver.user.admin.userpassword=21232F297A57A5A743894A0E4A801FC3
ftpserver.user.admin.homedirectory=/usr/local/ftp/
ftpserver.user.admin.enableflag=true
ftpserver.user.admin.writepermission=true
ftpserver.user.admin.maxloginnumber=0
ftpserver.user.admin.maxloginperip=0
ftpserver.user.admin.idletime=0
ftpserver.user.admin.uploadrate=0
ftpserver.user.admin.downloadrate=0

注意这里的ftp文件路径是/usr/local/ftp/,需要创建这个目录

mkdir /usr/local/ftp

3.配置连接端口

编辑文件res/conf/ftpd-typical.xml

vi res/conf/ftpd-typical.xml

在listeners节点下加入数据监听接口

<listeners>
	<nio-listener name="default" port="2121">
		<ssl>
			<keystore file="./res/ftpserver.jks" password="password" />
		</ssl>
		<data-connection idle-timeout="30">
		<active local-port="2120"/>
		<passive ports="2120" />
		</data-connection>
	</nio-listener>
</listeners>

这里配置的端口是2121和2120,所以防火墙iptables需要开启这两个端口其他机器才能访问这个ftp的2121端口

4.创建启动脚本并配置位开机启动

在bin目录下创建start.sh脚本,内容如下

/usr/local/apache-ftpd/bin/ftpd res/conf/ftpd-typical.xml &

保存并设置脚本可执行的权限

chmod +x bin/start.sh

开机启动,将启动脚本加入到开机执行脚本中

 vi /etc/rc.local

在文件中加入下面一行

/usr/local/apache-ftpd/bin/start.sh

这样开机的时候就会自动启动ftp服务了

也可以执行start.sh启动ftp 如

bin/start.sh

到此安装过程全部结束,如果想停止ftp服务只能通过ps找到进程将进程杀死.

以上配置完成之后ftp访问路径ftp://admin:admin@ip:2121/ 可以在浏览器地址栏输入进行验证ftp服务是否已经启动好。

spring mvc下载文件简单实现

文件下载有好多种方式,通过返回值,通过servlet的返回输出流等,我们这里使用spring的controller简单实现如何下载文件,并从url传入参数。

这是controller的简单实现,使用aspose.cells解析excel模板,并保存到输出流就可以下载了。

/**
* 下载文件,参数可以通过url传入
*/
@RequestMapping(value="/download/{fileName}.xls")
public void download(@RequestParam(required=false,value="arg1") String arg1,HttpServletRequest request, HttpServletResponse response){
	
	//解析excel模板
	WorkbookDesigner designer = new WorkbookDesigner();  
	String template_file_path = "d:/aspose/cell_sample.xls";  
	Workbook wb = new Workbook(template_file_path); 
	designer.setWorkbook(wb);
	
	//解析数据
	//designer.setDataSource("list", new MapData(getHashMapList()));//map list作为数据源
	//designer.process();//全自动赋值
	
	//文件输出到 输出流
	wb.save(response.getOutputStream(), SaveFormat.EXCEL_97_TO_2003);
	
}

在前端的脚本中直接使用url下载代码如下

window.location = escape("/download/myfile.xls?arg1=val1");

以上简易实现文件下载,注意如果是url传入中文的话,tomcat必须配置url的编码模式为utf-8,要不然的话在后台取到的参数会可能是乱码的。

aspose-word for java使用map作为数据源

aspose对word的操作和对excel的操作是分为两个独立的jar包实现的,同样的word模板的使用也是非常之方便,依然很遗憾默认并没有实现使用map数据作为数据源,这里我们来实现数据源接口IMailMergeDataSource来提供map数据源。

1.word模板,使用MergeFeild绑定数据

在word模板中通过菜单”插入→文档部件→域”插入MergeField域

1 2

根据上面插入域的方法,我们来创建一个简单的模板,使用一个map对象和一个map的列表

 

12

注意一个区域是以TableStart:和TableEnd:结束,冒号后面是数据源的名字.

2.实现IMailMergeDataSource接口提供map数据源

/**
 * 实现对HashMap的支持
 */
public class MapData implements IMailMergeDataSource  {

	@SuppressWarnings("rawtypes")
	private List<Map> dataList;

	private int index;

	// word模板中的«TableStart:tableName»«TableEnd:tableName»对应
	private String tableName = null;

	/**
	 * @param dataList
	 *            数据集
	 * @param tableName
	 *            与模板中的Name对应
	 */
	@SuppressWarnings("rawtypes")
	public MapData(List<Map> dataList, String tableName) {
		this.dataList = dataList;
		this.tableName = tableName;
		index = -1;
	}

	/**
	 * @param data
	 *            单个数据集
	 * @param tableName
	 *            与模板中的Name对应
	 */
	@SuppressWarnings("rawtypes")
	public MapData(Map data, String tableName) {
		if (this.dataList == null) {
			this.dataList = new ArrayList<Map>();
			this.dataList.add(data);
		}
		this.tableName = tableName;
		index = -1;
	}

	/**
	 * 获取结果集总数
	 * 
	 * @return
	 */
	private int getCount() {
		return this.dataList.size();
	}

	@Override
	public IMailMergeDataSource getChildDataSource(String arg0)
			throws Exception {
		return null;
	}

	@Override
	public String getTableName() throws Exception {
		return this.tableName;
	}

	/**
	 * 实现接口 获取当前index指向数据行的数据 将数据存入args数组中即可
	 * 
	 * @return ***返回false则不绑定数据***
	 */
	@Override
	public boolean getValue(String key, Object[] args) throws Exception {
		if (index < 0 || index >= this.getCount()) {
			return false;
		}
		if (args != null && args.length > 0) {
			args[0] = this.dataList.get(index).get(key);
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 实现接口 判断是否还有下一条记录
	 */
	@Override
	public boolean moveNext() throws Exception {
		index += 1;
		if (index >= this.getCount()) {
			return false;
		}
		return true;
	}


}

3.实现IMailMergeDataSourceRoot接口以提供多个数据源

/**
 * 提供多个数据源
 * @author Administrator
 *
 */
public class MapDataSet implements  IMailMergeDataSourceRoot {

	/**
	 * 多个数据源
	 */
	private Map<String,IMailMergeDataSource> ds = new HashMap<String,IMailMergeDataSource>();
	
	public MapDataSet(){}

	@Override
	public IMailMergeDataSource getDataSource(String arg0) throws Exception {
		if(this.ds.containsKey(arg0)){
			return this.ds.get(arg0);
		}
		return null;
	}
	/**
	 * 添加一个数据源
	 * @param tableName
	 * @param data
	 */
	public void add(IMailMergeDataSource data){
		try {
			this.ds.put(data.getTableName(), data);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

4.使用实现好的数据源解析word模板

	public static void main(String[] args) throws Exception{
		String template_file_path = "d:/aspose/word_sample.doc";  
		Document doc = new Document(template_file_path);

		//两个数据源
		MapDataSet ds = new MapDataSet();
		ds.add( new MapData(getHashMapData(),"map"));
		ds.add( new MapData(getHashMapList(),"list"));
		
		//主从数据源
		/*
		DataSet dataset = new DataSet();
		DataTable dt1 = mapListToDataTable(getHashMapList(),"dt1");
		DataTable dt2 = mapListToDataTable(getHashMapList(),"dt2");
		dataset.getTables().add(dt1);
		dataset.getTables().add(dt2);
		dataset.getRelations().add(new DataRelation("name_key",dt1.getColumns().get("name"), dt2.getColumns().get("name")));
		doc.getMailMerge().executeWithRegions(dataset);
		*/
		//没有数据的模板清楚掉
		doc.getMailMerge().setCleanupOptions(MailMergeCleanupOptions.REMOVE_UNUSED_REGIONS);
		//自动插入数据
		doc.getMailMerge().executeWithRegions(ds);
		
		doc.save("d:/aspose/word_sample_i.doc");
		
		System.out.println("ok!");
	}
	
	/**
	 * 单个数据
	 * @return
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private static HashMap getHashMapData(){
		
		HashMap data = new HashMap();
		
		data.put("name", "fullstacks");
		data.put("age", "30");
		
		return data;
		
	}
	
	/**
	 * hashMap 列表
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	private static List<Map> getHashMapList(){
		 List<Map> datas= new ArrayList<Map>();
		 datas.add(getHashMapData());
		 datas.add(getHashMapData());
		 datas.add(getHashMapData());
		 datas.add(getHashMapData());
		 return datas; 
	}

以上代码执行之后得到的效果如下

13

5.使用主从关系的列表

创建一个word模板如下

14

使用DataSet创建主从关系的数据结构,这里提供一个将Map列表转换为一个DataTable

	/**
	 * map列表转换为datatable
	 * @param data
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	public static DataTable mapListToDataTable(List<Map> datas,String tableName){
		if(datas == null || datas.size() == 0)return null;
		DataTable dt = new DataTable(tableName);
		Map fistrow = datas.get(0);
		for(Object key: fistrow.keySet()){
			dt.getColumns().add(new DataColumn((String)key,fistrow.get(key).getClass()));			
		}
		for(Map item:datas){
			DataRow dr = dt.newRow();
			for(Object key: item.keySet()){
				dr.set((String)key, item.get(key));
			}
			try {
				dt.getRows().add(dr);
			} catch (ConstraintException e) {
				e.printStackTrace();
			} catch (InvalidConstraintException e) {
				e.printStackTrace();
			}
		}	
		return dt;
	}

构建DataSet数据源

		DataSet dataset = new DataSet();
		DataTable dt1 = mapListToDataTable(getHashMapList(),"dt1");
		DataTable dt2 = mapListToDataTable(getHashMapList(),"dt2");
		dataset.getTables().add(dt1);
		dataset.getTables().add(dt2);
		dataset.getRelations().add(new DataRelation("name_key",dt1.getColumns().get("name"), dt2.getColumns().get("name")));
		doc.getMailMerge().executeWithRegions(dataset);

得到的效果如下

15

总结

以上是实现了一个通用的数据结构map作为aspose word的数据源,如果是结合spring jdbc的话,从数据库中查询出来的结果数据就是一个hashmap的list,这样就可以使用了,不必要转换为datatable那么麻烦,然后如果是多个数据源的话,就可以使用上面实现的MapDataSet实现,已提供多个数据源一次性把word模板解析完毕,如果是主从表的结构数据只能通过自带DataSet来完成了,上面已提供了一个静态方法方便把map列表转换为datatable,如果数据量比较大的话这样遍历可能效率低下,可以直接使用dataredder作为数据源,这样效率更高。

aspose-cells for java使用map作为数据源

使用excel作为模板导出数据是很普遍的应用,而且非常方便,aspose-cells是这样一个商业的软件,使用非常之方便,几行代码就可以搞定将数据自动插入到excel模板中,但是默认的并没有实现以map作为数据源,只是提供了一个借口ICellsDataTable,下面我们来实现这个接口。

1.实现接口ICellsDataTable支持Map的数据

/**
 * 使用hashmap作为数据源
 * @author Administrator
 *
 */
public class MapData implements ICellsDataTable {
	//数据集合  
    @SuppressWarnings("rawtypes")
	private List<Map> dataList = null;  

    //索引  
    private int index;  
      
    //存放dataList当中Map<String, Object>的key  
    private String[] columns = null;  
  
    @SuppressWarnings("rawtypes")
	public MapData(Map data) {  
        if(this.dataList == null) {  
            this.dataList = new ArrayList<Map>();  
        }  
        dataList.add(data);
    }  
      
    @SuppressWarnings("rawtypes")
	public MapData(List<Map> data) {  
        this.dataList = data;  
    }  
      
    /** 
     * 初始化方法 
     */  
    public void beforeFirst() {  
        index = -1;  
        columns = this.getColumns();  
    }  
  
    /** 
     * WorkbookDesigner自动调用 
     * 会将this.getColumns()方法所返回的列 按照顺序调用改方法 
     */  
    @SuppressWarnings("rawtypes")
	public Object get(int columnIndex) {  
        if(index < 0 || index >= this.getCount()) {  
            return null;  
        }  
        Map record = this.dataList.get(index);  
        String columnName = this.columns[columnIndex];  
        return record.get(columnName);  
    }  
  
    /** 
     * 根据columnName返回数据 
     */  
    @SuppressWarnings("rawtypes")
	public Object get(String columnName) {  
        Map record = this.dataList.get(index);  
        return record.get(columnName);  
    }  
  
    /** 
     * 获得列集合 
     */  
    @SuppressWarnings({ "rawtypes", "unchecked" })
	public String[] getColumns() {  
        Map temp = this.dataList.get(0);  
        Set<Entry> entrys = temp.entrySet();  
        List<String> columns = new ArrayList<String>();  
        for (Entry e : entrys) {  
            columns.add((String)e.getKey());  
        }  
        String[] s = new String[entrys.size()];  
        columns.toArray(s);  
        return s;  
    }  
  
    public int getCount() {  
        return this.dataList.size();  
    }  
  
    public boolean next() {  
        index += 1;  
        if(index >= this.getCount())  
        {  
            return false;  
        }  
        return true;  
    }  
  
}

2.使用的示例代码

我们来创建一个简单的excel模板,如下图所示

image

下面的代码将数据插入到这个模板中,这里我们做了两个数据源,分别命名为map和list,在模板的单元格中以&=开始加上数据源的名字加个点后面跟map里面的key名。格式就是&=name.key

public class ExcelUtils {

	public static void main(String[] args) throws Exception{		
		WorkbookDesigner designer = new WorkbookDesigner();  
		String template_file_path = "d:/aspose/cell_sample.xls";  
		Workbook wb = new Workbook(template_file_path); 
		designer.setWorkbook(wb);
				
		//给模板对象设置数据源  
		designer.setDataSource("map", new MapData(getHashMapData()));//hashmap对象作为数据源		
		//设置列表数据
		designer.setDataSource("list", new MapData(getHashMapList()));//map list作为数据源
		
		designer.process();//全自动赋值
		
		wb.save("d:/aspose/cell_sample_i.xls");//可以保存到数据流里面,以便下载使用
		System.out.println("ok!");
	}
	
	/**
	 * 单个数据
	 * @return
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private static HashMap getHashMapData(){
		
		HashMap data = new HashMap();
		
		data.put("name", "fullstacks");
		data.put("age", "30");
		
		return data;
		
	}
	
	/**
	 * hashMap 列表
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	private static List<Map> getHashMapList(){
		 List<Map> datas= new ArrayList<Map>();
		 datas.add(getHashMapData());
		 datas.add(getHashMapData());
		 datas.add(getHashMapData());
		 datas.add(getHashMapData());
		 return datas; 
	}
	
}

上面的模板插入数据之后的效果如下

image

以上就是完美使用map作为数据源的实现,当然也可以实现以其他数据作为数据源,java bean作为数据源是直接支持的了。

总结

商业的软件使用起来确实是非常方便,几行代码的事情,使用一些开源的东西可能稍微要写一些复杂的代码,那么使用excel模板一个最大的好处就是能够在模板里面吧excel格式和样式都配好,包括一些统计等等,只需要插入数据就可以了。

LINUX中IPTABLES和TC对端口的带宽限制

不管是iptables还是tc(traffic control)功能都很强大,都是与网络相关的工具,那么我们就利用这两个工具来对端口进行带宽的限制.

1.使用命令ifconfig查看服务器上的网卡信息,比如网卡eth0是用来对外的网络,也就是用户通过该网卡连接到系统,那么我们就对这个网卡进行带宽的限制

ifconfig

2.建立eth0队列

tc qdisc add dev eth0 root handle 1: htb default 20

命令解释:将一个htb队列绑定在eth0上,编号为1:0,默认归类是 20

3.建立跟分类

tc class add dev eth0 parent 1:0 calssid 1:1 htb  rate 3Mbit

命令解释:在队列1:0上创建根分类1:1 限速,类别htb,限速3Mbit

4.创建分类

tc class add dev eth0 parent 1:1 classid 1:20 htb rate 2Mbit ceil 3Mbit

以根分类1:1为父类创建分类1:20 ,类别为htb 限速 1Mbit 最大3Mbit(htb可借用其它类带宽)

5.添加公平队列

tc qdisc add dev eth0 parent 1:20 handle 20: sfq perturb 10

命令解释:sfq是公平队列 ,防止一个会话占用全部带宽

6.创建分类过滤器

tc filter add dev eth0 parent 1:20 protocol ip u32 match ip sport 8080 0xffff classid 1:20

命令解释:以分类1:20为父类创建编号为1:20的过滤器 ,加载u32模块,指定端口为8080

到此为止带宽就限制住了,最大带宽为3Mbit,也就是200多k的下载速度。

7.删除tc队列

tc qdisc del dev eth0 root

把建立好的队列删除,带宽不再限制。

 

以上只是使用tc进行对带宽的限制,当然也可以结合iptables使用,那么以上的第六步就不太一样了

6.创建过滤器并制定handle

tc filter add dev em2 parent 1:0 protocol ip prio 1 handle 1000 fw classid 1:20

7.使用iptable对端口绑定tc队列

iptables -t mangle -I POSTROUTING -o eth0 -sport 8080 -j MARK --set-mark 1000
iptables -t mangle -I POSTROUTING -o eth0 -sport 8080 -j RETURN

以上就是iptables与tc结合的结果,这里限制的是端口,当然也可以制定某个目标ip只限制制定ip的带宽。

 

总结

这里提到限制的是带宽,而且下载根据连接数进行平均分配,对TC而言,限制200k的话,一个连接那速度就是200k,如果两个连接就变成100k一个,而iptables的话,如果一个连接占满了200k的带宽,那么第二个连接就直接被拒绝了。

最终如果只是想对应用系统的下载的所有连接每个连接都限制在200k的下载速度的话,最好还是应用服务器自己进行限制。

Eclipse+Maven热部署调试

Eclipse JEE原生方式(WTP)调试Web应用的时候,当修改java类的时候,tomcat会自动重启,大部分的人都处于这种状态中,改点东西然后等待重启几十秒的时间,这是在浪费生命,修改资源文件不需要重启,其实java本身已经支持hot code replace热部署,以下是简单的配置。

maven必须使用tomcat插件

<plugin>
	<groupId>org.apache.tomcat.maven</groupId>
	<artifactId>tomcat7-maven-plugin</artifactId>
	<version>2.2</version>
</plugin>

eclipse中tomcat的server.xml的配置

image

文件中找到你项目的Context配置,设置reloadable=”false” 就可以了,默认是ture。

<Context docBase="stapp.web" path="/stapp.web" reloadable="false" source="org.eclipse.jst.jee.server:stapp.web"/>

修改完毕重启tomcat,然后修改java代码并保存,发现tomcat不在自动重启了,页面访问正常并执行了修改之后的代码,断点在新的代码上也没问题。

在Linux系统如何让程序开机时自动启动

系统的服务在开机时一般都可以自动启动,那在linux系统下如果想要程序在开机时自动启动怎么办?我们知道在 windows系统“开始”–>“所有程序”–>“启动”里面放个快捷方式就行,那Linux系统下呢?…系统的服务在开机时一般都可以自动启动,那在linux系统下如果想要程序在开机时自动启动怎么办?我们知道在 windows系统“开始”–>“所有程序”–>“启动”里面放个快捷方式就行,那Linux系统下呢?
这也是一个比较简单的问题,有不少的方法可以解决,这里介绍三种方法。因为是简单介绍,所以具体细节不是很详细,可以通过man看看相关手册。 

一、/etc/rc.local 

这是一个最简单的方法,编辑“/etc/rc.local”,把启动程序的shell命令输入进去即可(要输入命令的全路径),类似于windows下的“启动”。 

使用命令 vi /etc/rc.local 然后在文件最后一行添加要执行程序的全路径。 

例如,每次开机时要执行一个haha.sh,这个脚本放在/opt下面,那就可以在“/etc/rc.local”中加一行“/opt/./haha.sh”,或者两行“cd /opt”和“./haha.sh”。 

二、crontab(类似于windows的任务计划服务)

通过crontab可以设定程序的执行时间表,

例如让程序在每天的8点,或者每个星期一的10点执行一次。

 crontab -l 列出时间表;
crontab -e编辑时间表;
crontab -d删除时间表;

 “-l”没什么可说的,就是一个查看而已; 

“-e”是编辑,和vi没什么差别(其实就是用vi编辑一个特定文件); 

“-d”基本不用,因为它把该用户所有的时间表都删除了,一般都是用“-e”编辑把不要了的时间表逐行删除;

 那到底该如何编辑呢? 

crontab文件的格式是:M H D m d CMD。
一个6个字段,其中最后一个CMD就是所要执行的程序,

如haha.sh。
M:分钟(0-59)
H:小时(0-23)
D:日期(1-31)
m:月份(1-12)
d:一个星期中的某天(0-6,0代表周日)
这5个时间字段用空格隔开,其值可以是一个数字,也可以用逗号隔开的多个数字(或其他) ,如果不需设置,则默认为“*”。

 例如,每天的8点5分执行haha.sh,就是“5 8 * * * /opt/./haha.sh”。
好像和“开机程序自动启动”扯远了,现在回归正题。其实上面介绍的crontab的功能已经具备了开机自动启动的能力,可以写一个监测脚本,每5分钟执行一次(*/5 * * * * ./haha.sh),如果程序不在了就重新启动一次。 

编辑文件

#vi /etc/crontab

重启服务

#service crond restart

三、注册系统服务 操作系统自带的服务,如ssh,ftp等等,开机都是自动启动的,我们也可以通过这种方式让自己开发的程序提高“身价”。
  

比如我想把某个已经安装了的服务添加为系统服务,可以执行以下命令: chkconfig –add 服务名称 (首先,添加为系统服务,注意add前面有两个横杠)   chkconfig -leve 启动级别 服务名 on (说明,3级别代表在命令行模式启动,5级别代表在图形界面启动,on表示开启) chkconfig -leve 启动级别 服务名 off (说明,off表示关闭自启动) 

例如:chkconfig -level 3 mysql on (说明:让mysql服务在命令行模式,随系统启动) 也可以使用   chkconfig –add 服务名称    来删除系统服务  如果要查看哪些服务被添加为系统服务可以使用命令 : ntsysv 或者chkconfig –list   如果要查看哪些程序被添加为自启动,可以使用命令  : cat   /etc/rc.local (查看这个文件中添加了哪些程序路径)

 下面举例说说,如何把一个shell脚本添加为系统服务,并跟随系统启动: 可以看到“/etc/rc.d/init.d”下有很多的文件,每个文件都是可以看到内容的,其实都是一些shell脚本。 

系统服务的启动就是通过“/etc/rc.d/init.d”中的脚本文件实现的。我们也可以写一个自己的脚本放在这里。 

脚本文件的内容也很简单,类似于这个样子(例如起个名字叫做“hahad”): . /etc/init.d/functions start() {
echo “Starting my process ”
cd /opt
./haha.sh
}
stop() {
killall haha.sh
echo “Stoped”
}
写了脚本文件之后事情还没有完,

继续完成以下几个步骤: chmod +x hahad                    

#增加执行权限
chkconfig –add hahad            

 #把hahad添加到系统服务列表
chkconfig hahad on                 

#设定hahad的开关(on/off)
chkconfig –list hahad               

#就可以看到已经注册了hahad的服务 这时候才完成了全部工作。

OAuth 简介

OAuth 协议致力于使网站和应用程序(统称为消费方 Consumer)能够在无须用户透露其认证信息的情况下,通过 API 访问该用户在服务提供方(Service Provider)那里的受保护资源。更一般地说,OAuth 为 API 认证提供了一个可自由实现且通用的方法。目前互联网很多服务如 Open API 等都提供了 OAuth 认证服务,OAuth 标准也逐渐成为开放资源授权的标准。本文讨论如何使用 Google Code 上提供的 OAuth Java 库来实现基于 OAuth 认证的 Java 应用,并结合 Google 的 Data Service,给出使用 OAuth 方式访问 Google Data 的例子。

OAuth 简介

OAuth 是由 Blaine Cook、Chris Messina、Larry Halff 及 David Recordon 共同发起的,目的在于为 API 访问授权提供一个安全、开放的标准。

基于 OAuth 认证授权具有以下特点:

  • 安全。OAuth 与别的授权方式不同之处在于:OAuth 的授权不会使消费方(Consumer)触及到用户的帐号信息(如用户名与密码),也是是说,消费方无需使用用户的用户名与密码就可以申请获得该用户资源的授权。
  • 开放。任何消费方都可以使用 OAuth 认证服务,任何服务提供方 (Service Provider) 都可以实现自身的 OAuth 认证服务。
  • 简单。不管是消费方还是服务提供方,都很容易于理解与使用。

OAuth 的解决方案如下图所示。

图 1. OAuth Solution

图 1. OAuth Solution

如图 1 所示 OAuth 解决方案中用户、消费方及其服务提供方之间的三角关系:当用户需要 Consumer 为其提供某种服务时,该服务涉及到需要从服务提供方那里获取该用户的保护资源。OAuth 保证:只有在用户显式授权的情况下(步骤 4),消费方才可以获取该用户的资源,并用来服务于该用户。

从宏观层次来看,OAuth 按以下方式工作:

  1. 消费方与不同的服务提供方建立了关系。
  2. 消费方共享一个密码短语或者是公钥给服务提供方,服务提供方使用该公钥来确认消费方的身份。
  3. 消费方根据服务提供方将用户重定向到登录页面。
  4. 该用户登录后告诉服务提供方该消费方访问他的保护资源是没问题的。

OAuth 认证授权流程

在了解 OAuth 认证流程之前,我们先来了解一下 OAuth 协议的一些基本术语定义:

  • Consumer Key:消费方对于服务提供方的身份唯一标识。
  • Consumer Secret:用来确认消费方对于 Consumer Key 的拥有关系。
  • Request Token:获得用户授权的请求令牌,用于交换 Access Token。
  • Access Token:用于获得用户在服务提供方的受保护资源。
  • Token Secret:用来确认消费方对于令牌(Request Token 和 Access Token)的拥有关系。

图 2. OAuth 授权流程(摘自 OAuth 规范)

图 2. OAuth 授权流程(摘自 OAuth 规范)

对于图 2 具体每一执行步骤,解释如下:

  • 消费方向 OAuth 服务提供方请求未授权的 Request Token。
  • OAuth 服务提供方在验证了消费方的合法请求后,向其颁发未经用户授权的 Request Token 及其相对应的 Token Secret。
  • 消费方使用得到的 Request Token,通过 URL 引导用户到服务提供方那里,这一步应该是浏览器的行为。接下来,用户可以通过输入在服务提供方的用户名 / 密码信息,授权该请求。一旦授权成功,转到下一步。
  • 服务提供方通过 URL 引导用户重新回到消费方那里,这一步也是浏览器的行为。
  • 在获得授权的 Request Token 后,消费方使用授权的 Request Token 从服务提供方那里换取 Access Token。
  • OAuth 服务提供方同意消费方的请求,并向其颁发 Access Token 及其对应的 Token Secret。
  • 消费方使用上一步返回的 Access Token 访问用户授权的资源。

总的来讲,在 OAuth 的技术体系里,服务提供方需要提供如下基本的功能:

  • 第 1、实现三个 Service endpoints,即:提供用于获取未授权的 Request Token 服务地址,获取用户授权的 Request Token 服务地址,以及使用授权的 Request Token 换取 Access Token 的服务地址。
  • 第 2、提供基于 Form 的用户认证,以便于用户可以登录服务提供方做出授权。
  • 第 3、授权的管理,比如用户可以在任何时候撤销已经做出的授权。

而对于消费方而言,需要如下的基本功能:

  • 第 1、从服务提供方获取 Customer Key/Customer Secret。
  • 第 2、提供与服务提供方之间基于 HTTP 的通信机制,以换取相关的令牌。

我们具体来看一个使用 OAuth 认证的例子。

在传统的网站应用中,如果您想在网站 A 导入网站 B 的联系人列表,需要在网站 A 输入您网站 B 的用户名、密码信息。例如,您登陆 Plaxo (https://www.plaxo.com ),一个联系人管理网站,当您想把 GMail 的联系人列表导入到 Plaxo,您需要输入您的 GMail 用户名 / 密码,如图 3 所示:

图 3. 在 Plaxo 获得 GMail 联系人

图 3. 在 Plaxo 获得 GMail 联系人

在这里,Plaxo 承诺不会保存您在 Gmail 的密码。

如果使用 OAuth 认证,情况是不同的,您不需要向网站 A(扮演 Consumer 角色)暴露您网站 B(扮演 Service Provider 角色)的用户名、密码信息。例如,您登录 http://lab.madgex.com/oauth-net/googlecontacts/default.aspx 网站, 如图 4 所示:

图 4. 在 lab.madgex.com 获得 GMail 联系人

图 4. 在 lab.madgex.com 获得 GMail 联系人

点击“Get my Google Contacts”,浏览器将会重定向到 Google,引导您登录 Google,如图 5 所示:

图 5. 登录 Google

图 5. 登录 Google

登录成功后,将会看到图 6 的信息:

图 6. Google 对 lab.madgex.com 网站授权

图 6. Google 对 lab.madgex.com 网站授权

在您登录 Google,点击“Grant access”,授权 lab.madgex.com 后,lab.madgex.com 就能获得您在 Google 的联系人列表。

在上面的的例子中,网站 lab.madgex.com 扮演着 Consumer 的角色,而 Google 是 Service Provider,lab.madgex.com 使用基于 OAuth 的认证方式从 Google 获得联系人列表。

下一节,本文会给出一个消费方实现的例子,通过 OAuth 机制请求 Google Service Provider 的 OAuth Access Token,并使用该 Access Token 访问用户的在 Google 上的日历信息 (Calendar)。

示例

准备工作

作为消费方,首先需要访问 https://www.google.com/accounts/ManageDomains,从 Google 那里获得标志我们身份的 Customer Key 及其 Customer Secret。另外,您可以生成自己的自签名 X509 数字证书,并且把证书上传给 Google,Google 将会使用证书的公钥来验证任何来自您的请求。

具体的操作步骤,请读者参考 Google 的说明文档:http://code.google.com/apis/gdata/articles/oauth.html。

在您完成这些工作,您将会得到 OAuth Consumer Key 及其 OAuth Consumer Secret,用于我们下面的开发工作。

如何获得 OAuth Access Token

以下的代码是基于 Google Code 上提供的 OAuth Java 库进行开发的,读者可以从 http://oauth.googlecode.com/svn/code/java/core/ 下载获得。

  • 指定 Request Token URL,User Authorization URL,以及 Access Token URL,构造 OAuthServiceProvider 对象:
    OAuthServiceProvider serviceProvider = new OAuthServiceProvider( 
        "https://www.google.com/accounts/OAuthGetRequestToken", 
        "https://www.google.com/accounts/OAuthAuthorizeToken", 
        "https://www.google.com/accounts/OAuthGetAccessToken");
  • 指定 Customer Key,Customer Secret 以及 OAuthServiceProvider,构造 OAuthConsumer 对象:
    OAuthConsumer oauthConsumer = new OAuthConsumer(null 
        , "www.example.com" 
        , "hIsGkM+T4+90fKNesTtJq8Gs"
        , serviceProvider);
  • 为 OAuthConsumer 指定签名方法,以及提供您自签名 X509 数字证书的 private key。
    oauthConsumer.setProperty(OAuth.OAUTH_SIGNATURE_METHOD, OAuth.RSA_SHA1);
    oauthConsumer.setProperty(RSA_SHA1.PRIVATE_KEY, privateKey);
  • 由 OAuthConsumer 对象生成相应的 OAuthAccessor 对象:
     accessor = new OAuthAccessor(consumer);
  • 指定您想要访问的 Google 服务,在这里我们使用的是 Calendar 服务:
     Collection<? extends Map.Entry> parameters 
        = OAuth.newList("scope","http://www.google.com/calendar/feeds/");
  • 通过 OAuthClient 获得 Request Token:
    OAuthMessage response = getOAuthClient().getRequestTokenResponse( 
        accessor, null, parameters);

    使用 Request Token, 将用户重定向到授权页面,如图 7 所示:

  • 图 7. OAuth User Authorization

    图 7. OAuth User Authorization

  • 当用户点击“Grant access”按钮,完成授权后,再次通过 OAuthClient 获得 Access Token:
    oauthClient.getAccessToken(accessor, null, null);

在上述步骤成功完成后,Access Token 将保存在 accessor 对象的 accessToken 成员变量里。查看您的 Google Account 安全管理页面,可以看到您授权的所有消费方,如图 8 所示。

图 8. Authorized OAuth Access to your Google Account

图 8. Authorized OAuth Access to your Google Account

使用 OAuth Access Token 访问 Google 服务

接下来,我们使用上一节获得的 Access Token 设置 Google Service 的 OAuth 认证参数,然后从 Google Service 获取该用户的 Calendar 信息:

OAuthParameters para = new OAuthParameters(); 
para.setOAuthConsumerKey("www.example.com"); 
para.setOAuthToken(accessToken); 
googleService.setOAuthCredentials(para, signer);

清单 1 是完整的示例代码,供读者参考。

清单 1. 基于 OAuth 认证的 Google Service 消费方实现
import java.util.Collection; 
import java.util.Map; 
import net.oauth.OAuth; 
import net.oauth.OAuthAccessor; 
import net.oauth.OAuthConsumer; 
import net.oauth.client.OAuthClient; 

 public class DesktopClient { 
    private final OAuthAccessor accessor; 
    private OAuthClient oauthClient = null; 
    public DesktopClient(OAuthConsumer consumer) { 
        accessor = new OAuthAccessor(consumer); 
    } 

    public OAuthClient getOAuthClient() { 
        return oauthClient; 
    } 

    public void setOAuthClient(OAuthClient client) { 
        this.oauthClient = client; 
    } 

    //get the OAuth access token. 
    public String getAccessToken(String httpMethod, 	
	    Collection<? extends Map.Entry> parameters) throws Exception { 
        getOAuthClient().getRequestTokenResponse(accessor, null,parameters); 

        String authorizationURL = OAuth.addParameters( 
		    accessor.consumer.serviceProvider.userAuthorizationURL, 
			 OAuth.OAUTH_TOKEN, accessor.requestToken); 

        //Launch the browser and redirects user to authorization URL 
        Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " 
		    + authorizationURL); 

        //wait for user's authorization 
        System.out.println("Please authorize your OAuth request token. " 
		    + "Once that is complete, press any key to continue..."); 
        System.in.read(); 
        oauthClient.getAccessToken(accessor, null, null); 
        return accessor.accessToken; 
    } 
 } 

 import java.net.URL; 
 import java.security.KeyFactory; 
 import java.security.PrivateKey; 
 import java.security.spec.EncodedKeySpec; 
 import java.security.spec.PKCS8EncodedKeySpec; 
 import java.util.Collection; 
 import java.util.Map; 
 import com.google.gdata.client.GoogleService; 
 import com.google.gdata.client.authn.oauth.OAuthParameters; 
 import com.google.gdata.client.authn.oauth.OAuthRsaSha1Signer; 
 import com.google.gdata.client.authn.oauth.OAuthSigner; 
 import com.google.gdata.data.BaseEntry; 
 import com.google.gdata.data.BaseFeed; 
 import com.google.gdata.data.Feed; 
 import net.oauth.OAuth; 
 import net.oauth.OAuthConsumer; 
 import net.oauth.OAuthMessage; 
 import net.oauth.OAuthServiceProvider; 
 import net.oauth.client.OAuthClient; 
 import net.oauth.client.httpclient4.HttpClient4; 
 import net.oauth.example.desktop.MyGoogleService; 
 import net.oauth.signature.OAuthSignatureMethod; 
 import net.oauth.signature.RSA_SHA1; 

 public class GoogleOAuthExample { 
    //Note, use the private key of your self-signed X509 certificate. 
    private static final String PRIVATE_KEY = "XXXXXXXX"; 

    public static void main(String[] args) throws Exception { 
        KeyFactory fac = KeyFactory.getInstance("RSA"); 
        //PRIVATE_KEY is the private key of your self-signed X509 certificate. 
        EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec( 
		    OAuthSignatureMethod.decodeBase64(PRIVATE_KEY)); 
        fac = KeyFactory.getInstance("RSA"); 
        PrivateKey privateKey = fac.generatePrivate(privKeySpec); 
        OAuthServiceProvider serviceProvider = new OAuthServiceProvider( 
            //used for obtaining a request token 
			 //"https://www.google.com/accounts/OAuthGetRequestToken", 
	        //used for authorizing the request token 
            "https://www.google.com/accounts/OAuthAuthorizeToken", 
             //used for upgrading to an access token 
            "https://www.google.com/accounts/OAuthGetAccessToken"); 

        OAuthConsumer oauthConsumer = new OAuthConsumer(null 
            , "lszhy.weebly.com" //consumer key 
            , "hIsGnM+T4+86fKNesUtJq7Gs" //consumer secret 
            , serviceProvider); 

        oauthConsumer.setProperty(OAuth.OAUTH_SIGNATURE_METHOD, OAuth.RSA_SHA1); 
        oauthConsumer.setProperty(RSA_SHA1.PRIVATE_KEY, privateKey); 

        DesktopClient client = new DesktopClient(oauthConsumer); 
        client.setOAuthClient(new OAuthClient(new HttpClient4())); 
		
        Collection<? extends Map.Entry> parameters = 
		    OAuth.newList("scope","http://www.google.com/calendar/feeds/");
		
        String accessToken = client.getAccessToken(OAuthMessage.GET,parameters); 
		
		
        //Make an OAuth authorized request to Google 
		
        // Initialize the variables needed to make the request 
        URL feedUrl = new URL( 
		    "http://www.google.com/calendar/feeds/default/allcalendars/full");
        
        System.out.println("Sending request to " + feedUrl.toString()); 
        System.out.println(); 
        
        GoogleService googleService = new GoogleService("cl", "oauth-sample-app"); 

        OAuthSigner signer = new OAuthRsaSha1Signer(MyGoogleService.PRIVATE_KEY); 
        
        // Set the OAuth credentials which were obtained from the step above. 
        OAuthParameters para = new OAuthParameters(); 
        para.setOAuthConsumerKey("lszhy.weebly.com"); 
        para.setOAuthToken(accessToken); 
        googleService.setOAuthCredentials(para, signer); 
        
        // Make the request to Google 
        BaseFeed resultFeed = googleService.getFeed(feedUrl, Feed.class); 
        System.out.println("Response Data:");               
        System.out.println("=========================================="); 

        System.out.println("|TITLE: " + resultFeed.getTitle().getPlainText()); 
        if (resultFeed.getEntries().size() == 0) { 
           System.out.println("|\tNo entries found."); 
        } else { 
            for (int i = 0; i < resultFeed.getEntries().size(); i++) { 
               BaseEntry entry = (BaseEntry) resultFeed.getEntries().get(i); 
               System.out.println("|\t" + (i + 1) + ": "
                    + entry.getTitle().getPlainText()); 
            } 
        } 
        System.out.println("=========================================="); 	
    } 
 }

小结

OAuth 协议作为一种开放的,基于用户登录的授权认证方式,目前互联网很多 Open API 都对 OAuth 提供了支持,这包括 Google, Yahoo,Twitter 等。本文以 Google 为例子,介绍了 Java 桌面程序如何开发 OAuth 认证应用。在开发桌面应用访问 Web 资源这样一类程序时,一般通行的步骤是:使用 OAuth 做认证,然后使用获得的 OAuth Access Token,通过 REST API 访问用户在服务提供方的资源。

事实上,目前 OAuth 正通过许多实现(包括针对 Java、C#、Objective-C、Perl、PHP 及 Ruby 语言的实现)获得巨大的动力。大部分实现都由 OAuth 项目维护并放在 Google 代码库 (http://oauth.googlecode.com/svn/) 上。开发者可以利用这些 OAuth 类库编写自己需要的 OAuth 应用。

参考资料

Android中简单使用Sqlite

当数据比较复杂的时候需要保存数据到数据库中,那么Android中默认集成了sqlite数据库,这是一个被广泛使用的轻量数据库,在很多场合都使用到,Android中提供了很多便利的api来使用,下面来看看入门级别的几个api.

1.打开和关闭数据库

//打开或创建test.db数据库  
SQLiteDatabase db = openOrCreateDatabase("test.db", Context.MODE_PRIVATE, null); 

//...对数据的操作

//关闭当前数据库  
db.close();  
//删除test.db数据库  
deleteDatabase("test.db");

2.使用sql语句进行操作数据,使用db.execSQL(…)这个方法执行写好的sql

//删除表
db.execSQL("DROP TABLE IF EXISTS person");  
//创建person表  
db.execSQL("CREATE TABLE person (id INTEGER, name VARCHAR, age SMALLINT)");  
//插入数据  
db.execSQL("INSERT INTO person VALUES (?, ?, ?)", new Object[]{1,"fullstacks.cn",28}); 
//查询数据
Cursor c = db.rawQuery("SELECT * FROM person WHERE age >= ?", new String[]{"20"});  
while (c.moveToNext()) {  
	int id = c.getInt(c.getColumnIndex("id"));  
	String name = c.getString(c.getColumnIndex("name"));  
	int age = c.getInt(c.getColumnIndex("age"));  
	Log.i("db", "id=>" + id + ", name=>" + name + ", age=>" + age);  
}
//删除数据
db.execSQL("DELETE FROM person WHERE id = ?", new String[]{1});
//更新数据
db.execSQL("UPDATE person set age = ? WHERE id = ?", new String[]{29,1});

3.使用android提供的api进行增删改

//新增
//ContentValues以键值对的形式存放数据  
ContentValues cv = new ContentValues();  
cv.put("name", "www.fullstacks.cn");  
cv.put("age", 30);  
cv.put("id", 2); 
//插入ContentValues中的数据  
db.insert("person", null, cv); 

//删除
db.delete("person", "age < ?", new String[]{"30"}); 

//更新数据
cv = new ContentValues();  
cv.put("age", 35);  //这是更新的内容
db.update("person", cv, "name = ?", new String[]{"john"}); //后面的两个参数是更新的条件

//查询数据还是使用sql查询 db.rawQuery

数据库创建之后文件存放在 /data/data/[PACKAGE_NAME]/databases目录下,可以使用文件浏览器查看

以上的几个简单api对基本的数据操作就已经满足了,更多的操作比如对数据库的升级维护之类的,更多的游标操作等可以查看api.

总结

本质上sqlite数据库在移动应用中的使用和服务端的使用没什么区别,也可以做一些实体和数据之间的映射,在移动应用中这些复杂的数据访问层并不是很需要,所以很多数据访问层的服务端框架并没有在Android上实现,使用这些基本的api都已经全部搞定,最多就是做一些简单的封装。