Android中多图片选择器PhotoPicker库的使用(仿微信) - CSDN博客

mikel阅读(1709)

来源: Android中多图片选择器PhotoPicker库的使用(仿微信) – CSDN博客

PhotoPicker

基于 donglua/PhotoPickerlovetuzitong/MultiImageSelector 修改的一个图片选择类库。

Gradle

compile 'com.foamtrace:photopicker:1.0'

效果图

   

使用方法

ImageConfig

可选属性, 用于过滤照片列表信息。

ImageConfig config = new ImageConfig();
config.minHeight = 400;
config.minWidth = 400;
config.mimeType = new String[]{"image/jpeg", "image/png"}; // 图片类型 image/gif ...
config.minSize = 1 * 1024 * 1024; // 1Mb 图片大小

单选

PhotoPickerIntent intent = new PhotoPickerIntent(MainActivity.this);
intent.setSelectModel(SelectModel.SINGLE);
intent.setShowCarema(true); // 是否显示拍照, 默认false
// intent.setImageConfig(config);
startActivityForResult(intent, REQUEST_CAMERA_CODE);

多选

PhotoPickerIntent intent = new PhotoPickerIntent(MainActivity.this);
intent.setSelectModel(SelectModel.MULTI);
intent.setShowCarema(true); // 是否显示拍照, 默认false
intent.setMaxTotal(9); // 最多选择照片数量,默认为9
intent.setSelectedPaths(imagePaths); // 已选中的照片地址, 用于回显选中状态
// intent.setImageConfig(config);
startActivityForResult(intent, REQUEST_CAMERA_CODE);

拍照

private ImageCaptureManager captureManager;

btnCarema.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        try {
            if(captureManager == null){
                captureManager = new ImageCaptureManager(MainActivity.this);
            }
            Intent intent = captureManager.dispatchTakePictureIntent();
            startActivityForResult(intent, ImageCaptureManager.REQUEST_TAKE_PHOTO);
        } catch (IOException e) {
            Toast.makeText(MainActivity.this, R.string.msg_no_camera, Toast.LENGTH_SHORT).show();
            e.printStackTrace();
        }
    }
});

预览

PhotoPreviewIntent intent = new PhotoPreviewIntent(MainActivity.this);
intent.setCurrentItem(position); // 当前选中照片的下标
intent.setPhotoPaths(imagePaths); // 已选中的照片地址
startActivityForResult(intent, REQUEST_PREVIEW_CODE);

ActivityResult

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if(resultCode == RESULT_OK) {
        switch (requestCode) {
            // 选择照片
            case REQUEST_CAMERA_CODE:
                refreshAdpater(data.getStringArrayListExtra(PhotoPickerActivity.EXTRA_RESULT));
                break;
            // 拍照
            case ImageCaptureManager.REQUEST_TAKE_PHOTO:
                if(captureManager.getCurrentPhotoPath() != null) {
                    captureManager.galleryAddPic();
                    // 照片地址
                    String imagePaht = captureManager.getCurrentPhotoPath();
                    // ...
                }
                break;
            // 预览
            case REQUEST_PREVIEW_CODE:
                refreshAdpater(data.getStringArrayListExtra(PhotoPreviewActivity.EXTRA_RESULT));
                break;
        }
    }
}

private void refreshAdpater(ArrayList<String> paths){
    // 处理返回照片地址
}

配置信息

style.xml

<!-- 设置ActionBar菜单字体 -->
<style name="ActionMenuTextStyle" parent="TextAppearance.AppCompat.Widget.ActionBar.Menu">
    <item name="android:textSize">@dimen/action_text_size</item>
    <item name="android:textStyle">normal</item>
</style>

<style name="actionBarTheme" parent="ThemeOverlay.AppCompat.Dark.ActionBar">
</style>

<style name="actionBarPopupTheme" parent="ThemeOverlay.AppCompat.Light">
</style>

<style name="PhotoPickerTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="windowActionBar">false</item>
    <item name="windowNoTitle">true</item>
    <item name="android:windowBackground">@color/photopicker_background</item>
    <item name="actionMenuTextAppearance">@style/ActionMenuTextStyle</item>
    <!--<item name="actionBarTheme">@style/actionBarTheme</item>-->
    <!--<item name="actionBarPopupTheme">@style/actionBarPopupTheme</item>-->
</style>

AndroidManifest.xml

<activity
    android:name="com.foamtrace.photopicker.PhotoPickerActivity"
    android:theme="@style/PhotoPickerTheme"
    android:configChanges="orientation|screenSize"/>

<activity android:name="com.foamtrace.photopicker.PhotoPreviewActivity"
    android:theme="@style/PhotoPickerTheme"/>

转载地址:

https://github.com/wzfu/PhotoPicker

源码下载:

http://download.csdn.net/detail/zhaihaohao1/9545045

腾讯浏览服务X5内核集成 - 简书

mikel阅读(1448)

来源: 腾讯浏览服务X5内核集成 – 简书

一、什么是X5内核?

用官方文档的话说,就是解决系统webview兼容性差、加载速度慢、功能缺陷等问题,腾讯QQ浏览器大神们产出的一个WebView可以替换原生WebView。前面不重要,关键是开源的,还能解决我们开发中的一些问题和需求,举个例子:QQ浏览器独立小窗播放视频的功能,还有WebView播放原生H5Video难以很好解决的全屏播放问题~~~~….用X5都能解决。官方文档入口http://x5.tencent.com/doc?id=1004

二、需求场景

解决WebView播放H5 Video全屏问题

尝试过的其他方法

重写WebChromeClient的onShowCustomView开启全屏;onHideCustomView退出全屏。使用过这个方法的人,都应该知道Android4.4以后,不会在走这个回调

三、开始搬砖

准备工作:
集成准备工作就不多说了,步骤太简单不过了,就两个文件,一个tbs_sdk….的lib包,一个32位的liblbs.so库。直接可以从官方文档那里下。
需要提一下,如果是Eclipse用户,so库的导入,需要在工程libs目录创建armeabi文件夹,把so库放armeabi目录里(libs——>armeabi);如果是AndroidStudio目录,则直接在main目录下创建jniLib,然后在jniLibs目录下创建armeabi文件夹,再把so放armeabi目录(main——>jniLibs——>armeabi)

简单使用(直接上代码):

X5的WebView还原度非常高,使用起来和原生的WebView基本上是一模一样

MainActiviy

 @Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // Set layout
    getWindow().setFormat(PixelFormat.TRANSLUCENT);
    setContentView(R.layout.activity_main);
    mWebView = (com.tencent.smtt.sdk.WebView) findViewById(R.id.forum_context);
    mWebView.getSettings().setJavaScriptEnabled(true);// 支持js
    mWebView.getSettings().setUseWideViewPort(true); //自适应屏幕
    mWebView.loadUrl("http://res.ky-express.com/h5/video/72.html");
}

activity_main

<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<com.tencent.smtt.sdk.WebView
    android:id="@+id/forum_context"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:paddingLeft="5dp"
    android:paddingRight="5dp"/>

</RelativeLayout>

这个很简单,几分钟就能看到X5的效果了,但是这样只能在部分手机上运行,不能适配大多数手机,原因在于我们只创建并armeabi的目录,也就是说只有arm cpu的手机上才能有效果。

四、优化

1、上述问题,适配所有的cpu的机型

解决方案也很简单,把其他cpu的目录也创建出来就ok了(mips,mips64,x86,x86_64,armeabi-v7a,armeabi,arm64-v8a);第二步就是把上面那个liblbs.so库依次放置到各个cpu目录下即可;最最关键的最后一步,在build.config文件中的defaultConfig里面加上这行配置代码

ndk {
        abiFilters"armeabi","armeabi-v7a","x86","mips"
    }

下面是我项目的build.config配置

defaultConfig {
    applicationId "spanlogistics.oxbix.com.webviewfullscreen"
    minSdkVersion 14
    targetSdkVersion 23
    versionCode 1
    versionName "1.0"

    ndk {
        abiFilters"armeabi","armeabi-v7a","x86","mips"
    }
}

重新运行一下,应该就解决勒。

2、体验的问题

初次启动打开页面时,会有个白屏的过程,大概4秒钟左右(手机配置差的话可能会更长)
原因:使用X5内核需要有个初始化的过程,我们Demo中是直接用的,并未做预初始化的操作,换句话说,如果预初始化完成后,在去打开该就不会出现白屏体验了

解决方案:在我们应用的Application里面去对X5进行预初始化,我们创建一个服务去预加载它,然后在Application中去开启该服务

预初始化X5的服务代码,PreLoadX5Service
 @Nullable
@Override
public IBinder onBind(Intent intent) {
    return null;
}

@Override
public void onCreate() {
    super.onCreate();
    initX5();
    preinitX5WebCore();
}

private void initX5() {
    QbSdk.initX5Environment(getApplicationContext(), QbSdk.WebviewInitType.FIRSTUSE_AND_PRELOAD, cb);
    Log.d("gggbbb","预加载中...");
}

QbSdk.PreInitCallback cb = new QbSdk.PreInitCallback() {

    @Override
    public void onViewInitFinished(boolean arg0) {
        // TODO Auto-generated method stub
        Log.e("0912", " onViewInitFinished is " + arg0);
    }

    @Override
    public void onCoreInitFinished() {
        // TODO Auto-generated method stub

    }
};
 private void preinitX5WebCore() {

    if(!QbSdk.isTbsCoreInited()) {

        // preinit只需要调用一次,如果已经完成了初始化,那么就直接构造view

        QbSdk.preInit(MainActivity.this, null);// 设置X5初始化完成的回调接口

    }
}

Application代码

@Override
public void onCreate() {
    super.onCreate();
    initX5();
}
private void initX5() {
    Intent intent = new Intent(this, PreLoadX5Service.class);
    startService(intent);
}

3、我们会发现集成X5后,项目编译变慢了,如果项目太大的话,还不容易跑起来,可以在主module的build.gradle里面加上下面这段代码试试

 dexOptions {
    javaMaxHeapSize "4g"
    preDexLibraries = false
}

4、一般情况下根据上面操作,就已经完美集成勒,但是自己集成过程中很容易掉的一个坑,就是64位手机上不能使用X5,官方文档中也提到了相应了解决方案,就是把32位的so库直接放到64位的文件夹中当64位使用

补充

看到不少童鞋再问如何去除QQ浏览器推广,其实评论里已经有童鞋给了解决方案,可能有的小伙伴没能看到,在这里做个补充,先感谢下 @StoneHui 童鞋。下面是去除QQ浏览器推广的代码:

 getWindow().getDecorView().addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
            @Override
            public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
                ArrayList<View> outView = new ArrayList<View>();
                getWindow().getDecorView().findViewsWithText(outView,"QQ浏览器",View.FIND_VIEWS_WITH_TEXT);
                int size = outView.size();
                if(outView!=null&&outView.size()>0){
                    outView.get(0).setVisibility(View.GONE);
                }
            }
     });

怎么加呢?
很简单,直接在使用到X5 Webview的那个Activity里面加上这段代码就ok了

作者:_陈小平
链接:https://www.jianshu.com/p/8a7224ff371a
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Android 使用腾讯X5 Webview浏览器拍照或从相册上传图片 - CSDN博客

mikel阅读(1276)

来源: Android 使用腾讯X5 Webview浏览器拍照或从相册上传图片 – CSDN博客

最近在项目开发中,需要使用WebView上传文件。默认情况下情况下,使用Android的WebView是不能够支持上传文件的。

经过查找资料,得知需要重新WebChromeClient,根据选择到的文件Uri,传给页面去上传就可以了。

自定义WebChromeClient

先在WebViewActivity里面自定义MyWebChromeClient,代码如下:

public class MyWebChromeClient extends WebChromeClient {
    // For Android 3.0+
    public void openFileChooser(ValueCallback<Uri> uploadMsg) {
        CLog.i("UPFILE", "in openFile Uri Callback");
        if (mUploadMessage != null) {
            mUploadMessage.onReceiveValue(null);
        }
        mUploadMessage = uploadMsg;
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        i.setType("*/*");
        startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
    }
    // For Android 3.0+
    public void openFileChooser(ValueCallback uploadMsg, String acceptType) {
        CLog.i("UPFILE", "in openFile Uri Callback has accept Type" + acceptType);
        if (mUploadMessage != null) {
            mUploadMessage.onReceiveValue(null);
        }
        mUploadMessage = uploadMsg;
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        String type = TextUtils.isEmpty(acceptType) ? "*/*" : acceptType;
        i.setType(type);
        startActivityForResult(Intent.createChooser(i, "File Chooser"),
                FILECHOOSER_RESULTCODE);
    }
    // For Android 4.1
    public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
        CLog.i("UPFILE", "in openFile Uri Callback has accept Type" + acceptType + "has capture" + capture);
        if (mUploadMessage != null) {
            mUploadMessage.onReceiveValue(null);
        }
        mUploadMessage = uploadMsg;
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        String type = TextUtils.isEmpty(acceptType) ? "*/*" : acceptType;
        i.setType(type);
        startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
    }
//Android 5.0+
    @Override
    @SuppressLint("NewApi")
    public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
        if (mUploadMessage != null) {
            mUploadMessage.onReceiveValue(null);
        }
        CLog.i("UPFILE", "file chooser params:" + fileChooserParams.toString());
        mUploadMessage = filePathCallback;
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        if (fileChooserParams != null && fileChooserParams.getAcceptTypes() != null
                && fileChooserParams.getAcceptTypes().length > 0) {
            i.setType(fileChooserParams.getAcceptTypes()[0]);
        } else {
            i.setType("*/*");
        }
        startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
        return true;
    }
}

上面openFileChooser是系统未暴露的接口,因此不需要加Override的注解,同时不同版本有不同的参数,其中的参数,第一个ValueCallback用于我们在选择完文件后,接收文件回调到网页内处理,acceptType为接受的文件mime type。在Android 5.0之后,系统提供了onShowFileChooser来让我们实现选择文件的方法,仍然有ValueCallback,在FileChooserParams参数中,同样包括acceptType。我们可以根据acceptType,来打开系统的或者我们自己创建文件选择器。当然如果需要打开相机拍照,也可以自己去使用打开相机拍照的Intent去打开即可。

处理选择的文件

因为我们前面是使用startActivityForResult来打开的选择页面,我们会在onActivityResult中接收到选择的结果。代码如下:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == FILECHOOSER_RESULTCODE) {

        Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
        if (result == null) {
            mUploadMessage.onReceiveValue(null);
            mUploadMessage = null;
            return;
        }

        String path =  FileUtils.getPath(this, result);
        if (TextUtils.isEmpty(path)) {
            mUploadMessage.onReceiveValue(null);
            mUploadMessage = null;
            return;
        }
        Uri uri = Uri.fromFile(new File(path));

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            mUploadMessage.onReceiveValue(new Uri[]{uri});
        } else {
            mUploadMessage.onReceiveValue(uri);
        }
        mUploadMessage = null;
    }
}

注意事项:

  1. 由于不同版本的差别,Android 5.0以下的版本,ValueCallback 的onReceiveValue接收的参数类型是Uri, 5.0及以上版本接收的是Uri数组,在传值的时候需要注意。
  2. 选择文件会使用系统提供的组件或者其他支持的app,返回的uri有的直接是文件的url,有的是contentprovider的uri,因此我们需要统一处理一下,转成文件的uri,可参考以下代码(获取文件的路径)。
  3. 即使获取的结果为null,也要传给webview,即直接调用mUploadMessage.onReceiveValue(null),否则网页会阻塞。
  4. 在打release包的时候,因为我们会混淆,要特别设置不要混淆WebChromeClient子类里面的openFileChooser方法,由于不是继承的方法,所以默认会被混淆,然后就无法选择文件了。

FileUtils工具类如下:

public class FileUtils {
    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is ExternalStorageProvider.
     */
    public static boolean isExternalStorageDocument(Uri uri) {
        return "com.android.externalstorage.documents".equals(uri.getAuthority());
    }
    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is DownloadsProvider.
     */
    public static boolean isDownloadsDocument(Uri uri) {
        return "com.android.providers.downloads.documents".equals(uri.getAuthority());
    }
    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is MediaProvider.
     */
    public static boolean isMediaDocument(Uri uri) {
        return "com.android.providers.media.documents".equals(uri.getAuthority());
    }
    /**
     * Get the value of the data column for this Uri. This is useful for
     * MediaStore Uris, and other file-based ContentProviders.
     *
     * @param context The context.
     * @param uri The Uri to query.
     * @param selection (Optional) Filter used in the query.
     * @param selectionArgs (Optional) Selection arguments used in the query.
     * @return The value of the _data column, which is typically a file path.
     */
    public static String getDataColumn(Context context, Uri uri, String selection,
                                       String[] selectionArgs) {
        Cursor cursor = null;
        final String column = "_data";
        final String[] projection = {
                column
        };
        try {
            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
                    null);
            if (cursor != null && cursor.moveToFirst()) {
                final int column_index = cursor.getColumnIndexOrThrow(column);
                return cursor.getString(column_index);
            }
        } finally {
            if (cursor != null)
                cursor.close();
        }
        return null;
    }
    /**
     * Get a file path from a Uri. This will get the the path for Storage Access
     * Framework Documents, as well as the _data field for the MediaStore and
     * other file-based ContentProviders.
     *
     * @param context The context.
     * @param uri The Uri to query.
     * @author paulburke
     */
    @SuppressLint("NewApi")
    public static String getPath(final Context context, final Uri uri) {
        final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
        // DocumentProvider
        if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
            // ExternalStorageProvider
            if (isExternalStorageDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];
                if ("primary".equalsIgnoreCase(type)) {
                    return Environment.getExternalStorageDirectory() + "/" + split[1];
                }
                // TODO handle non-primary volumes
            }
            // DownloadsProvider
            else if (isDownloadsDocument(uri)) {
                final String id = DocumentsContract.getDocumentId(uri);
                final Uri contentUri = ContentUris.withAppendedId(
                        Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
                return getDataColumn(context, contentUri, null, null);
            }
            // MediaProvider
            else if (isMediaDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];
                Uri contentUri = null;
                if ("image".equals(type)) {
                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                } else if ("video".equals(type)) {
                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                } else if ("audio".equals(type)) {
                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                }
                final String selection = "_id=?";
                final String[] selectionArgs = new String[] {
                        split[1]
                };
                return getDataColumn(context, contentUri, selection, selectionArgs);
            }
        }
        // MediaStore (and general)
        else if ("content".equalsIgnoreCase(uri.getScheme())) {
            return getDataColumn(context, uri, null, null);
        }
        // File
        else if ("file".equalsIgnoreCase(uri.getScheme())) {
            return uri.getPath();
        }
        return null;
    }
}

看了上面的代码,你是不是感觉有点复杂呢?下面我们将介绍怎么通过使用腾讯X5 Webview浏览器实现拍照或从相册上传图片功能。

使用腾讯X5 Webview浏览器

TBS腾讯浏览器服务官网:http://x5.tencent.com
jar包下载:http://x5.tencent.com/doc?id=1004

集成教程:
http://www.jianshu.com/p/8a7224ff371a
http://blog.csdn.net/qq_17387361/article/details/52396338
http://www.jianshu.com/p/e4009688119b

环境调好后,我们就可以愉快的开始调试了。

public class MyWebChromeClient extends WebChromeClient {
           @Override
            public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> valueCallback, FileChooserParams fileChooserParams) {
                TLog.error("onShowFileChooser");
                return super.onShowFileChooser(webView, valueCallback, fileChooserParams);
            }

            // Android > 4.1.1 调用这个方法
            public void openFileChooser(ValueCallback<Uri> uploadMsg,
                                        String acceptType, String capture) {
                mUploadMessage = uploadMsg;
                choosePicture();

            }

            // 3.0 + 调用这个方法
            public void openFileChooser(ValueCallback<Uri> uploadMsg,
                                        String acceptType) {
                mUploadMessage = uploadMsg;
                choosePicture();

            }

            // Android < 3.0 调用这个方法
            public void openFileChooser(ValueCallback<Uri> uploadMsg) {
                mUploadMessage = uploadMsg;
                choosePicture();
            }
}

这里选择图片使用了三方图片选择组件:PhotoPicker,项目地址:https://github.com/donglua/PhotoPicker

其中choosePicture方法如下,

private void choosePicture() {
        PhotoPicker.builder()
                .setPhotoCount(1)
                .setShowCamera(true)
                .setShowGif(true)
                .setPreviewEnabled(false)
                .start(TBSWebActivity.this, PhotoPicker.REQUEST_CODE);
    }

在onActivityResult中接收到选择的结果,处理如下:

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
        if (null == mUploadMessage) {
            return;
        }
        if (resultCode == RESULT_OK && requestCode == PhotoPicker.REQUEST_CODE) {
            ArrayList<String> photos = intent.getStringArrayListExtra(PhotoPicker.KEY_SELECTED_PHOTOS);

            Uri result = Uri.parse(photos.get(0));
            mUploadMessage.onReceiveValue(result);
            mUploadMessage = null;
        } else {
            mUploadMessage.onReceiveValue(null);
        }
    }

这里再强调下,即使获取的结果为null(比如按back键取消了),也要传给webview,即直接调用mUploadMessage.onReceiveValue(null),否则网页会阻塞。

H5前端

最后简单再说一下H5前端调用。

<div class="btn1">上传照片
    <input type="file"  accept="image/*" id="uploadImage" capture="camera" onchange="selectFileImage(this);"> 
</div>

上传相关的js代码如下:

var arr=new Array();

function selectFileImage(fileObj) {
    var file = fileObj.files['0'];
    //图片方向角 added by lzk
    var Orientation = null;

    if (file) {
        console.log("正在上传,请稍后...");
        var rFilter = /^(image\/jpeg|image\/png)$/i; // 检查图片格式
        if (!rFilter.test(file.type)) {
            //showMyTips("请选择jpeg、png格式的图片", false);
            return;
        }
        // var URL = URL || webkitURL;
        //获取照片方向角属性,用户旋转控制
        EXIF.getData(file, function() {
           // alert(EXIF.pretty(this));
            EXIF.getAllTags(this); 
            //alert(EXIF.getTag(this, 'Orientation')); 
            Orientation = EXIF.getTag(this, 'Orientation');
            //return;
        });

        var oReader = new FileReader();
        oReader.onload = function(e) {
            //var blob = URL.createObjectURL(file);
            //_compress(blob, file, basePath);
            var image = new Image();
            image.src = e.target.result;
            image.onload = function() {
                var expectWidth = this.naturalWidth;
                var expectHeight = this.naturalHeight;

                if (this.naturalWidth > this.naturalHeight && this.naturalWidth > 640) {
                    expectWidth = 640;
                    expectHeight = expectWidth * this.naturalHeight / this.naturalWidth;
                } else if (this.naturalHeight > this.naturalWidth && this.naturalHeight > 640) {
                    expectHeight = 640;
                    expectWidth = expectHeight * this.naturalWidth / this.naturalHeight;
                }
                //alert(expectWidth+','+expectHeight);
                var canvas = document.createElement("canvas");
                var ctx = canvas.getContext("2d");
                canvas.width = expectWidth;
                canvas.height = expectHeight;
                ctx.drawImage(this, 0, 0, expectWidth, expectHeight);
                //alert(canvas.width+','+canvas.height);

                var base64 = null;
                var mpImg = new MegaPixImage(image);
                    mpImg.render(canvas, {
                        maxWidth: 640,
                        maxHeight: 640,
                        quality: 0.8,
                        orientation: Orientation
                    });

                base64 = canvas.toDataURL("image/jpeg", 0.8);
                //alert(base64);
                //var img = 
                //修复ios
                if (navigator.userAgent.match(/iphone/i)) {
                    console.log('iphone');
                    //alert(expectWidth + ',' + expectHeight);
                    //如果方向角不为1,都需要进行旋转 added by lzk
                    /*if(Orientation != "" && Orientation != 1){
                        alert('旋转处理');
                        switch(Orientation){
                            case 6://需要顺时针(向左)90度旋转
                                alert('需要顺时针(向左)90度旋转');
                                rotateImg(this,'left',canvas);
                                break;
                            case 8://需要逆时针(向右)90度旋转
                                alert('需要顺时针(向右)90度旋转');
                                rotateImg(this,'right',canvas);
                                break;
                            case 3://需要180度旋转
                                alert('需要180度旋转');
                                rotateImg(this,'right',canvas);//转两次
                                rotateImg(this,'right',canvas);
                                break;
                        }       
                    }*/

                    /*var mpImg = new MegaPixImage(image);
                    mpImg.render(canvas, {
                        maxWidth: 800,
                        maxHeight: 1200,
                        quality: 0.8,
                        orientation: Orientation
                    });
                    base64 = canvas.toDataURL("image/jpeg", 0.8);*/
                }else if (navigator.userAgent.match(/Android/i)) {// 修复android
                    /*var encoder = new JPEGEncoder();
                    base64 = encoder.encode(ctx.getImageData(0, 0, expectWidth, expectHeight), 80);*/
                }else{
                    //alert(Orientation);
                    /*if(Orientation != "" && Orientation != 1){
                        //alert('旋转处理');
                        switch(Orientation){
                            case 6://需要顺时针(向左)90度旋转
                                alert('需要顺时针(向左)90度旋转');
                                rotateImg(this,'left',canvas);
                                break;
                            case 8://需要逆时针(向右)90度旋转
                                alert('需要顺时针(向右)90度旋转');
                                rotateImg(this,'right',canvas);
                                break;
                            case 3://需要180度旋转
                                alert('需要180度旋转');
                                rotateImg(this,'right',canvas);//转两次
                                rotateImg(this,'right',canvas);
                                break;
                        }       
                    }*/
                    /*var mpImg = new MegaPixImage(image);
                    mpImg.render(canvas, {
                        maxWidth: 800,
                        maxHeight: 1200,
                        quality: 0.8,
                        orientation: Orientation
                    });

                    base64 = canvas.toDataURL("image/jpeg", 0.8);*/
                }
                var imgName = uploadImage(base64);
                //alert("img===="+imgName);

                $(".overlayer").css("display","none");
                if($(fileObj).attr("flag")=="myImage"){
                    arr[0]=base64;
                    $("#myImage").attr("src", base64);
                    $("#myImage").css("opacity","1");
                    ajax_pram.img0 = imgName;
                }else{
                    arr[1]=base64;
                    $("#myImage1").attr("src", base64);
                    $("#myImage1").css("opacity","1");
                    ajax_pram.img1 = imgName;
                    }
                    input_validate();

            };
        };
        oReader.readAsDataURL(file);
    }
}


//对图片旋转处理 added by lzk
function rotateImg(img, direction,canvas) {
        //alert(img);
        //最小与最大旋转方向,图片旋转4次后回到原方向  
        var min_step = 0;  
        var max_step = 3;  
        //var img = document.getElementById(pid);  
        if (img == null)return;  
        //img的高度和宽度不能在img元素隐藏后获取,否则会出错  
        /*var height = img.height;  
        var width = img.width;  */
        var height = canvas.height;  
        var width = canvas.width;  
       // alert(width+','+height);
        //var step = img.getAttribute('step');  
        var step = 2;  
        if (step == null) {  
            step = min_step;  
        }  
        if (direction == 'right') {  
            step++;  
            //旋转到原位置,即超过最大值  
            step > max_step && (step = min_step);  
        } else {  
            step--;  
            step < min_step && (step = max_step);  
        }  
        //img.setAttribute('step', step);  
        /*var canvas = document.getElementById('pic_' + pid);  
        if (canvas == null) {  
            img.style.display = 'none';  
            canvas = document.createElement('canvas');  
            canvas.setAttribute('id', 'pic_' + pid);  
            img.parentNode.appendChild(canvas);  
        }  */
        //旋转角度以弧度值为参数  
        var degree = step * 90 * Math.PI / 180;  
        var ctx = canvas.getContext('2d');  
        switch (step) {  
            case 0:  
                canvas.width = width;  
                canvas.height = height;  
                ctx.drawImage(img, 0, 0);  
                break;  
            case 1:  
                canvas.width = height;  
                canvas.height = width;  
                ctx.rotate(degree);  
                ctx.drawImage(img, 0, -height);  
                break;  
            case 2:  
                canvas.width = width;  
                canvas.height = height;  
                ctx.rotate(degree);  
                ctx.drawImage(img, -width, -height);  
                break;  
            case 3:  
                canvas.width = height;  
                canvas.height = width;  
                ctx.rotate(degree);  
                ctx.drawImage(img, -width, 0);  
                break;  
        }  
    }  

/** 记录上传数据 */
function uploadImage(imageData) {
    if (imageData == undefined) {
        alert('没有要上传的图片');
        return false;
    }
    var result;
    $.ajax({
        type: "post",
        url: 'http://upload.domain.cn/index.php?r=v1/certificates/saveimg',
        data: {'baseStr':imageData},
        async:false,
        success: function(data) {
            result = data
        }
    });
    return result;
}

如果使用过程中有问题,请留言反馈。csdn代码排版始终不舒服,请大家包涵!

Layui中的table通过函数控制数据加载刷新

mikel阅读(1145)

            function usable(id, usable) {
                $.post('/Supplier/changeusable', { id: id, usable: usable }, function (result) {
                    if (result.status) {                        
                        layer.alert("更新成功", { icon: 6 });
                         layui.use(['table', 'element'], function () {
                            layui.table.reload('datatable');
                         });   

                    } else {
                        layer.alert(result.message);
                    }
                },'json');
            }

[Delphi-码农]Delphi EXCEL导入 - CSDN博客

mikel阅读(1406)

来源: [Delphi-码农]Delphi EXCEL导入 – CSDN博客

 

整理过的

  1. procedure TParameterALterFines.btnImportExcelClick(Sender: TObject);
  2. const
  3.   BeginRow = 2; BeginCol = 1;
  4. var
  5.   Excel: OleVariant;
  6.   iRow,iCol : integer;
  7.   xlsFilename: string;
  8.   ExcelRowCount  : integer;
  9. begin
  10.   OpenDialog1.Title := ‘请选择正确的excel文件’;
  11.   OpenDialog1.Filter := ‘Excel(*.xls)|*.xls’;
  12.   if OpenDialog1.Execute then
  13.     edit1.Text := OpenDialog1.FileName;
  14.   if (trim(edit1.Text) = then
  15.   begin
  16.     GetActiveWindow();
  17.     showmessage( ‘请选择正确的excel路径’);
  18.     exit;
  19.   end;
  20.   xlsFilename := trim(edit1.Text);
  21.   try
  22.     Excel := CreateOLEObject(‘Excel.Application’);
  23.   except
  24.     showmessage(‘excel没有安装’);
  25.     Exit;
  26.   end;
  27.   Excel.Visible := false;
  28.   Excel.WorkBooks.Open(xlsFilename);
  29.   ExcelRowCount := Excel.WorkSheets[1].UsedRange.Rows.Count;
  30.   //showmessage(inttoStr(ExcelRowCount));
  31.   try
  32.     iRow := BeginRow;
  33.     iCol := BeginCol;
  34.     while trim(Excel.WorkSheets[1].Cells[iRow,iCol].value) <>  do
  35.     //WHILE iRow<ExcelRowCount Do
  36.     begin
  37.       with ADOQuery1 do
  38.       begin
  39.         //Append;
  40.         //Fields[0].AsString := trim(Excel.WorkSheets[1].Cells[iRow,iCol].value);
  41.         //Fields[1].AsString := trim(Excel.WorkSheets[1].Cells[iRow,iCol+1].value);
  42.         //Fields[2].Asstring := trim(Excel.WorkSheets[1].Cells[iRow,iCol+2].value);
  43.           close;
  44.           SQL.clear;
  45.           SQL.Text:=‘insert into Temp (ID,Content) ‘;
  46.           SQL.Text:=sql.Text+‘select   ‘+trim(Excel.WorkSheets[1].Cells[iRow,iCol].value);
  47.           sql.Text:=sql.Text+‘ , ‘+  trim(Excel.WorkSheets[1].Cells[iRow,iCol+1].value);
  48.           execsql;
  49.           iRow := iRow + 1;
  50.       end;
  51.     end;
  52.     Excel.Quit;
  53.     ADOQuery1.UpdateStatus ;
  54.   except
  55.     showmessage(‘导入数据出错’);
  56.     Excel.Quit;
  57.     //Exit;
  58.   end;
  59.   //MessageBox(GetActiveWindow(), ‘数据导入成功’, ‘提示信息’, MB_OK +  MB_ICONWARNING);
  60. end;

 

http://zhidao.baidu.com/link?url=hNhGnrIAKOWJEIICoZwdKtcof7zyj_LMp-PlV2xmjgRIdAKSWDRuPT3xxzqGdhSlZc6NXsKSckbPSOdk7cBJ2a

  1. 元接口部分引用 comobj 单元(uses )
  2. procedure TForm1.Button1Click(Sender: TObject);
  3. var excelx,excely : string;
  4. begin
  5.     try
  6.       ExcelApp := CreateOleObject(‘Excel.Application’);
  7.       WorkBook := ExcelApp.WorkBooks.Open(opendialog.FileName);//使用opendialog对话框指定
  8. //excel档路径
  9. ExcelApp.Visible := false;
  10.       ExcelRowCount := WorkBook.WorkSheets[1].UsedRange.Rows.Count;
  11.       for i := 1 to excelrowcount + 1 do
  12.       begin
  13.         excelx := excelapp.Cells[i,1].Value;
  14.         excely := excelapp.Cells[i,2].Value;
  15.         if ((excelapp.Cells[i,1].Value  = and (ExcelApp.Cells[i,2].Value = )) then
  16. //指定excel档的第 i 行 ,第 1,2(看情况而定)行如果为空就退出,这样的设定,最好是你的档案力这两行//对应数据库中不能为空的数据
  17.           exit
  18.         else
  19.         with query1 do
  20.         begin
  21.           close;
  22.           sql.clear;
  23. sql.add(insert into test(name,address) values(:name,:address));
  24. parambyname(‘name’).asstring := excelx;//excel档的第一列插入到test表的 name栏位;
  25. parambyname(‘address’).asstring := excely;//excel档的第二列插入到test表的 address 栏位;
  26. execsql;
  27.         end;
  28.       end;
  29.       finally
  30.         WorkBook.Close;
  31.         ExcelApp.Quit;
  32.         ExcelApp := Unassigned;
  33.         WorkBook := Unassigned;
  34.     end;
  35.   end;

 

Delphi Excel导入 的通用程序 .

  1. Delphi Excel导入 的通用程序 .
  2. 分类: delphi 20120924 18:19 127人阅读 评论(0) 收藏 举报
  3. exceldelphiintegerprocedure TForm1.btnClick(Sender: TObject);
  4. begin
  5.   OpenDialog1.Title := ‘请选择正确的excel文件’;
  6.   OpenDialog1.Filter := ‘Excel(*.xls)|*.xls’;
  7.   if OpenDialog1.Execute then
  8.   edit1.Text := OpenDialog1.FileName;
  9. end;
  10. procedure TForm1.btninClick(Sender: TObject);
  11. const
  12.   BeginRow = 2; BeginCol = 1;
  13. var
  14.   Excel: OleVariant;
  15.   iRow,iCol : integer;
  16.   xlsFilename: string;
  17. begin
  18. if (trim(edit1.Text) = then
  19.   begin
  20.   MessageBox(GetActiveWindow(), 请选择正确的excel路径’, MB_OK +
  21.   MB_ICONWARNING);
  22.   exit;
  23.   end;
  24.   xlsFilename := trim(edit1.Text);
  25.   try
  26.   Excel := CreateOLEObject(‘Excel.Application’);
  27.   except
  28.   Application.MessageBox(‘excel没有安装’‘提示信息’, MB_OK+MB_ICONASTERISK+MB_DEFBUTTON1+MB_APPLMODAL);
  29.   Exit;
  30.   end;
  31.   Excel.Visible := false;
  32.   Excel.WorkBooks.Open(xlsFilename);
  33.   try
  34.   iRow := BeginRow;
  35.   iCol := BeginCol;
  36.   while trim(Excel.WorkSheets[1].Cells[iRow,iCol].value) <>  do begin
  37.   with ADOQuery1 do begin
  38.   Append;
  39.   Fields[0].AsString := trim(Excel.WorkSheets[1].Cells[iRow,iCol].value);
  40.   Fields[1].AsString := trim(Excel.WorkSheets[1].Cells[iRow,iCol+1].value);
  41.   Fields[2].Asstring := trim(Excel.WorkSheets[1].Cells[iRow,iCol+2].value);
  42.   iRow := iRow + 1;
  43.   end;
  44.   end;
  45.   Excel.Quit;
  46.   ADOQuery1.UpdateStatus ;
  47.   except
  48.   Application.MessageBox(‘导入数据出错’‘提示信息’, MB_OK+MB_ICONASTERISK+MB_DEFBUTTON1+MB_APPLMODAL);
  49.   Excel.Quit;
  50.   end;
  51.   MessageBox(GetActiveWindow(), ‘数据导入成功’‘提示信息’, MB_OK +
  52.   MB_ICONWARNING);
  53. end;

Oracle 用户(user)和模式(schema)的区别 - 一路NET - 博客园

mikel阅读(934)

来源: Oracle 用户(user)和模式(schema)的区别 – 一路NET – 博客园

原文:Oracle 用户(user)和模式(schema)的区别

概述:

(一)什么Oracle叫用户(user):

A user is a name defined in the database that can connect to and access objects.

大意:Oracle用户是用连接数据库和访问数据库对象的。(用户是用来连接数据库访问数据库)。

(二)什么叫模式(schema):

A schema is a collection of database objects (used by a user.). Schema objects are the logical structures that directly refer to the database’s data.
大意:模式是数据库对象的集合。模式对象是数据库数据的逻辑结构。(把数据库对象用模式分开成不同的逻辑结构)。

(三)用户(user)与模式(schema)的区别:
Schemas and users help database administrators manage database security.
大意:用户是用来连接数据库对象。而模式用是用创建管理对象的。(模式跟用户在oracle 是一对一的关系。)

 


 

详解:

从官方的定义中,我们可以看出schema为数据库对象的集合。为了区分各个集合,我们需要给这个集合起个名字,这些名字就是我们在企业管理器的方案下看到 的许多类似用户名的节点,这些类似用户名的节点其实就是一个schema。

schema里面包含了各种对象如tables, views, sequences, stored procedures, synonyms, indexes, clusters, and database links。

 

一个用户一般对应一个schema,该用户的schema名等于用户名,并作为该用户缺省schema。这也就是我们在企业管理器的方案下看 到schema名都为数据库用户名的原因。而Oracle数据库中不能新创建一个schema,要想创建一个schema,只能通过创建一个用户的方法解决 (Oracle中虽然有create schema语句,但是它并不是用来创建一个schema的),在创建一个用户的同时为这个用户创建一个与用户名同名的schem并作为该用户的缺省 shcema。

即schema的个数同user的个数相同,而且schema名字同user名字一一 对应并且相同,所有我们可以称schema为user的别名,虽然这样说并不准确,但是更容易理解一些。

一个用户有一个缺省的schema,其schema名就等于用户名,当然一个用户还可以使用其他的schema。如果我们访问一个表时,没有指明该 表属于哪一个schema中的,系统就会自动给我们在表上加上缺省的sheman名。比如我们在访问数据库时,访问scott用户下的emp表,通过 select * from emp; 其实,这SQL语句的完整写法为select * from scott.emp。

在数据库中一个对象的完整名称为schema.object,而不属user.object。类似如果我们在创建对象时不指定该对象 的schema,在该对象的schema为用户的缺省schema。这就像一个用户有一个缺省的表空间,但是该用户还可以使用其他的表空间,如果我们在创 建对象时不指定表空间,则对象存储在缺省表空间中,要想让对象存储在其他表空间中,我们需要在创建对象时指定该对象的表空间。

在Android浏览器中通过WebView调用相机拍照/选择文件 上传到服务器 - CSDN博客

mikel阅读(1566)

来源: 在Android浏览器中通过WebView调用相机拍照/选择文件 上传到服务器 – CSDN博客

最近做的一个项目中,有这样一个要求,在浏览器中调用系统的拍照功能或者选择文件,然后将文件上传到服务器,类似修改头像。        简单而言,就是在一个html页面中有这样一段代码 <input class=”filePrew” type=”file” capture=”camera” accept=”image/*” name=”image”>

刚开始的时候,没有感觉很难的,因为在UC浏览器、系统自带的浏览器中都可以进行拍照/文件管理器选择,可是在自己所写的Activity中却不行。后来实在是没有思路了,就在网上找了一下,发现要        实现这种功能,都是在webview的WebChromeClient中覆盖掉openFileChooser方法,注意openFileChooser方法在WebChromeClient中有@hide标记。这里只管重写即可,下面将主要代码贴出来,做个记录

private ValueCallback<Uri> mUploadFile;  
    /**拍照/选择文件请求码*/  
    private static final int REQUEST_UPLOAD_FILE_CODE = 12343;  
    private void setWebChromeClient()  
    {  
        if (null != mMainWebView)  
        {  
            mMainWebView.setWebChromeClient(new WebChromeClient()  
            {  
                // Andorid 4.1+  
                public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType, String capture)  
                {  
                    openFileChooser(uploadFile);  
                }  
  
                // Andorid 3.0 +  
                public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType)  
                {  
                    openFileChooser(uploadFile);  
                }  
  
                // Android 3.0  
                public void openFileChooser(ValueCallback<Uri> uploadFile)  
                {  
                    // Toast.makeText(WebviewActivity.this, "上传文件/图片",Toast.LENGTH_SHORT).show();  
                    mUploadFile = uploadFile;  
                    startActivityForResult(Intent.createChooser(createCameraIntent(), "Image Browser"), REQUEST_UPLOAD_FILE_CODE);  
                }  
            });  
        }  
    }  
  
    private Intent createCameraIntent()  
    {  
        Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);//拍照  
        //=======================================================  
        Intent imageIntent = new Intent(Intent.ACTION_GET_CONTENT);//选择图片文件  
        imageIntent.setType("image/*");  
        //=======================================================  
        return cameraIntent;  
    }  
    //最后在OnActivityResult中接受返回的结果  
    protected void onActivityResult(int requestCode, int resultCode, Intent data)  
    {  
        if (requestCode == REQUEST_UPLOAD_FILE_CODE && resultCode == RESULT_OK)  
        {  
            if (null == mUploadFile)  
            {  
                return;  
            }  
            Uri result = (null == data) ? null : data.getData();  
            if (null != result)  
            {  
                ContentResolver resolver = this.getContentResolver();  
                String[] columns = { MediaStore.Images.Media.DATA };  
                Cursor cursor = resolver.query(result, columns, null, null, null);  
                cursor.moveToFirst();  
                int columnIndex = cursor.getColumnIndex(columns[0]);  
                String imgPath = cursor.getString(columnIndex);  
                System.out.println("imgPath = " + imgPath);  
                if (null == imgPath)  
                {  
                    return;  
                }  
                File file = new File(imgPath);  
                   //将图片处理成大小符合要求的文件  
                result = Uri.fromFile(handleFile(file));  
                mUploadFile.onReceiveValue(result);  
                mUploadFile = null;       
            }  
        }  
        super.onActivityResult(requestCode, resultCode, data);  
    }  
    /**处理拍照/选择的文件*/  
    private File handleFile(File file)  
    {  
        DisplayMetrics dMetrics = getResources().getDisplayMetrics();  
        BitmapFactory.Options options = new Options();  
        options.inJustDecodeBounds = true;  
         BitmapFactory.decodeFile(file.getAbsolutePath(), options);  
        int imageWidth = options.outWidth;  
        int imageHeight = options.outHeight;  
        System.out.println("  imageWidth = " + imageWidth + " imageHeight = " + imageHeight);  
        int widthSample = (int) (imageWidth / (dMetrics.density * 90));  
        int heightSample = (int) (imageHeight / (dMetrics.density * 90));  
        System.out.println("widthSample = " + widthSample + " heightSample = " + heightSample);  
        options.inSampleSize = widthSample < heightSample ? heightSample : widthSample;  
        options.inJustDecodeBounds = false;  
        Bitmap newBitmap = BitmapFactory.decodeFile(file.getAbsolutePath(), options);  
        System.out.println("newBitmap.size = " + newBitmap.getRowBytes() * newBitmap.getHeight());  
        File handleFile = new File(file.getParentFile(), "upload.png");  
        try  
        {  
            if (newBitmap.compress(CompressFormat.PNG, 50, new FileOutputStream(handleFile)))  
            {  
                System.out.println("保存图片成功");  
            }  
        }  
        catch (FileNotFoundException e)  
        {  
            e.printStackTrace();  
        }  
  
        return handleFile;  
  
    }  

这样就可以在WebView中上传文件了。记得要添加相应的权限!
参考:http://developer.Android.com/about/versions/Android-3.0.html

http://blog.sina.com.cn/s/blog_5749ead90101clrn.html

Android WebView中打开相机拍照和选择相册 - CSDN博客

mikel阅读(2554)

来源: Android WebView中打开相机拍照和选择相册 – CSDN博客

一般在项目中与js交互,可能会遇到上传文件图片等操作,避免不了一些坑,下面简单说一下,Android 在不同版本中webView调用相机,选择相册的方法是不一样的,3.0以下的调用

  1. public void openFileChooser(ValueCallback<Uri> uploadMsg)

3.0以上:

  1. public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType)

4.4以下:

  1. public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture)

5.0以上:

  1. public boolean onShowFileChooser(WebView mWebView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams)

这里有个漏洞,4.4.x的由于系统内核发生了改变,没法调用以上方法,现在仍然找不到解决办法,唯一的方法就是4.4直接使用手机浏览器打开,这个是可以的。

那下面具体的贴下代码:

继承自WebChromeClient,重写

//3.0++
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
    openFileChooserImpl(uploadMsg);
}

//3.0--
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
    openFileChooserImpl(uploadMsg);
}

//4.1
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {

    openFileChooserImpl(uploadMsg);
}


@Override
public boolean onShowFileChooser(WebView mWebView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
    openFileChooserImplForAndroid5(filePathCallback);

    return true;
}

外层定义接收返回值:

private ValueCallback<Uri> mUploadMessage;
private ValueCallback<Uri[]> mUploadCallbackAboveL;

不同的版本调不同的方法:

private void openFileChooserImplForAndroid5(ValueCallback<Uri[]> uploadMsg) {
    mUploadCallbackAboveL = uploadMsg;
    dispatchTakePictureIntent();
}

//5.0以下的掉用
private void openFileChooserImpl(ValueCallback<Uri> uploadMsg) {
    mUploadMessage = uploadMsg;
    dispatchTakePictureIntent();
}

//拍照
private void dispatchTakePictureIntent() {
    selectImgDialog();

}

 

我下面就把代码全部贴出来吧:

   private void takePhoto() {
        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        if (takePictureIntent.resolveActivity(getActivity().getPackageManager()) != null) {
            Uri imageUri = null;
            try {
                imageUri = Uri.fromFile(createImageFile());
            } catch (IOException e) {
                e.printStackTrace();
            }
            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
            startActivityForResult(takePictureIntent, FILECHOOSER_RESULTCODE);
        }
    }

    /**
     * 209.
     * 本地相册选择图片
     * 210.
     */
    private void chosePic() {
        Intent innerIntent = new Intent(Intent.ACTION_GET_CONTENT);
        String IMAGE_UNSPECIFIED = "image/*";
        innerIntent.setType(IMAGE_UNSPECIFIED); // 查看类型
        Intent wrapperIntent = Intent.createChooser(innerIntent, null);
        startActivityForResult(wrapperIntent, REQ_CHOOSE);
    }


    String mCurrentPhotoPath = null;
    String FileName = "forum";

    //创建文件夹包装图片
    private File createImageFile() throws IOException {
        File storageDir = new File(Util.getAppPath(getActivity()) + FileName);

        if (!storageDir.exists()) {
            storageDir.mkdirs();
        }
        storageDir = new File(Util.getAppPath(getActivity()) + FileName + "/", System.currentTimeMillis() + ".jpg");
        //保存当前图片路径
        mCurrentPhotoPath = storageDir.getAbsolutePath();
        return storageDir;
    }

    //onActivityResult回调
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == FILECHOOSER_RESULTCODE || requestCode == REQ_CHOOSE) {
            if (null == mUploadMessage && null == mUploadCallbackAboveL) return;
            Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
            if (mUploadCallbackAboveL != null) {
                onActivityResultAboveL(requestCode, data);
            } else if (mUploadMessage != null) {
                mUploadMessage.onReceiveValue(result);
                mUploadMessage = null;
            }
        }

    }

    //5.0以上版本,由于api不一样,要单独处理
//    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private void onActivityResultAboveL(int requestCode, Intent data) {

        if (mUploadCallbackAboveL == null) {
            return;
        }
        Uri result = null;
        if (requestCode == FILECHOOSER_RESULTCODE) {
            File file = new File(mCurrentPhotoPath);
            Uri localUri = Uri.fromFile(file);
            Intent localIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, localUri);
            getActivity().sendBroadcast(localIntent);
            result = Uri.fromFile(file);

        } else if (requestCode == REQ_CHOOSE) {
            result = data.getData();
        }
        mUploadCallbackAboveL.onReceiveValue(new Uri[]{result});
        mUploadCallbackAboveL = null;
        return;
    }

 

后补的代码:

private void openFileChooserImplForAndroid5(ValueCallback<Uri[]> uploadMsg) {
    mUploadCallbackAboveL = uploadMsg;
    dispatchTakePictureIntent();
}

//5.0以下的掉用
private void openFileChooserImpl(ValueCallback<Uri> uploadMsg) {
    mUploadMessage = uploadMsg;
    dispatchTakePictureIntent();
}

 

基本代码就是以上这些,selectImgDialog()方法是弹窗提示选择,关于这个弹窗布局的用法,之前写的一篇文章说的很清楚了(BottomSheet 的详解及注意事项),有疑问的欢迎提问。

利用HTML5+的API实现基于MUI的文件上传_dssy的一点儿笔记_新浪博客

mikel阅读(1516)

利用HTML5+的API实现基于MUI的文件上传_dssy的一点儿笔记_新浪博客,dssy的一点儿笔记,

来源: 利用HTML5+的API实现基于MUI的文件上传_dssy的一点儿笔记_新浪博客

项目中需要用到图片上传,昨天搞整了一下,对我这个菜鸟来说,还是有很多值得注意的地方。

h5+提供了可以调用原生安卓API的功能,当然也能调用camera。需要注意的是,拍照后的保存路径如果不自己设置的话,是使用的hbuilder默认的路径。不过,我把路径打印出来,在genymotion里面居然没有翻到。还有一点,通过h5+调用拍照后照片也不会保存到相册​,但是在真机上是会自动保存的,为了以防万一,可以设置图片保存到本地相册。不多说,贴代码。

页面打开时需要在plus中预加载,这里新建文件夹camera

plus.camera.getCamera返回摄像头管理对象,之后可以对拍照参数进行设置,拍照成功会调用回调函数。使用resolveLocalFileSystemURL可以通过URL参数获取目录对象或文件对象,把entry转换成本地路径之后就可以保存啦。

拍摄照片

通过相册选择照片的方式和获取摄像头的操作差不多,在这里我给了一个f1的全局数组变量。用来存放选择的照片。哦,单选的话就不用数组了可以。多选和单选,可以通过GalleryOptions对象来进行设置:multiple: true.还可以进行好多好多设置。

相册选择照片

好了现在可以进行图片上传了,在这里使用的是二进制流的形式进行上传,同时为了节约资源,有必要对图片进行压缩,这个代码我是扒的DCould论坛上面的。利用canvas对图片进行重绘,要压缩多小,修改w = 320 || w;的数字就行了,我这里是320,300多K的图片传到后台也就150K了。不过可以更小,最好注意按比例压缩。哦,这里是需要导入JQuery.min.js的包的,attr是JQuery下的方法,不然用不了。每次压缩完成之后,用push方法把图片流追加进数组就行了。

压缩图片

好了可以上传啦,上传利用的是mui封装的post方法,跟ajax其实是一样的。h5+其实提供了很多上传方法,比如有一个uploader的接口,也可用来上传,C#的话,可以用handler来接收,不过我不怎么熟悉,需要好好研究研究。

上传

接下来就是后台接收了,在这里使用的是mvc,前端直接请求action,返回图片数组,在转换成图片之前,base64的格式需要把开头一段去掉。不然会报格式错误的。在保存进文件夹的时候,之前代码老是报错:GDI+中发生一般性错误,就是这个。在网上搜索了好久,解决办法为克隆一个bitmap对象,同时Dispose来及时释放掉打开的文件(C#的话使用using{}来限定也可以),不过这个解决方法的应用场景为从文件夹读出然后写入。图片上传还用不到克隆bitmap,最后在我们老大的提示下终于发现,iis对文件夹的写入进行了限制,需要去开启权限,就是要把everyone的权限添加上去,同时还要赋予完全控制权限。这样就可以顺利进行文件写入的操作啦。

c#后台接收

贴一个克隆bitmap的方法,与这次的图片上传没啥关系,留着以后能用到。

bitmap转换

​       最后,我还要感谢老大,以及其他小伙伴,在工作中给了我很多帮助,有这么一群小伙伴在一起真的太好啦!

微信第三方平台微信支付配置没有rootca.pem根证书文件的解决办法-蜘蛛网博客

mikel阅读(2031)

微信第三方平台微信支付配置没有rootca.pem根证书文件的解决办法我们在通过微信第三方平台制作微信活动的时候,很多情况下都需要用到微信支付接口,例如商城类的微信功能,微砍价、微助力、微秒杀、微拼团等功能插件,以及三级分销和微商城直销,都是需要配置微信支付接口的,而涉及到微信红包功能,还需要我们上传微信支付证书,也就是把官方提供的证书上传到微信第三方平台里面,但是最近微信商户平台网站更新了,从3

来源: 微信第三方平台微信支付配置没有rootca.pem根证书文件的解决办法-蜘蛛网博客

信第三方平台微信支付配置没有rootca.pem根证书文件的解决办法

我们在通过微信第三方平台制作微信活动的时候,很多情况下都需要用到微信支付接口,例如商城类的微信功能,微砍价、微助力、微秒杀、微拼团等功能插件,以及三级分销和微商城直销,都是需要配置微信支付接口的,而涉及到微信红包功能,还需要我们上传微信支付证书,也就是把官方提供的证书上传到微信第三方平台里面,但是最近微信商户平台网站更新了,从3月6日起不在提供rootca.pem文件,也就是根证书文件,统一由微信第三方平台服务器提供,一些平台由于功能更新不及时,在上传微信支付证书的时候还是要求用户上传根证书文件(rootca.pem),于是用户就懵了,这个文件到底在哪里呢,别急,继续往下看。
微信第三方平台微信支付配置怎么没有rootca.pem根证书文件

微信人家平台在收到官方的信息后,第一时间更新了程序,网站后台统一为大家提供rootca.pem根证书文件,请点击下面的链接下载rootca.pem根证书文件。

再说一遍,本文件通用,通用,通用

注意一下,这个rootca.pem根证书文件同样适用于其他的微信第三方平台,如果您现在使用的微信第三方还没有更新这个功能,可以下载小编分享出来的文件,上传到你配置证书的地方就可以了。

目前来看,只有微信红包功能是需要上传微信支付证书文件的,包括但不限于微信红包、微信合体红包、微信到店红包、摇一摇红包、微信裂变红包(粉丝红包)、H5游戏红包等功能。由于不同的微信第三方平台开发的功能不一样,就算功能近乎一致,起的名字也未必相同,所以大家记住一点,只要是涉及到微信红包这一块的功能都是需要上传支付证书的。而新注册的公众号,比如认证服务号,如果是刚开通了微信支付功能,那么到微信商户平台申请红包接口以后是需要等3个月才能在微信第三方平台去发红包,若你正好是这一类情况,那您当前就没有必要去上传证书这些东西了,因为即使你上传好了,微信支付接口也都配置好,还是要等到官方规定的时候到了才可以使用发红包的功能。