精华内容
下载资源
问答
  • 这就是一个可以联网的图库软件,下面我们来...数据库中保存图片文件路径图片标题URL地址5.listview中列出已保存的所有条目,添加条目后,同步展现到listview中6.选中listview中一个条目,点击删除,删除存储的条目

    先看效果图:

    这里写图片描述
    这里写图片描述
    这里写图片描述
    这里写图片描述

    这就是一个可以联网的图库软件,下面我们来看看需求


    业务需求

    1.判断是否第一次运行,第一次运行,提示添加新条目

    2.点击添加按钮,弹出对话框,输入图片网址和标题

    3.下载图片保存到本地SD卡中

    4.数据库中保存图片文件路径和图片标题和URL地址

    5.listview中列出已保存的所有条目,添加条目后,同步展现到listview中

    6.选中listview中一个条目,点击删除,删除存储的条目,同步展现到listview

    7.长按listview的条目,弹出删除菜单项,点击菜单项也可以删除条目

    8.提供contentprovider供其他软件访问数据库


    问题分析

    虽然要求看起来挺多但是可以大致分为3部分去实现

    1. 数据的下载保存,包括存储到本地sdcard 和数据库中
    2. 数据的删除
    3. 为其他软件提供数据库接口

    主要代码实现

    首先用户输入图片地址我们应该去下载图片并保存到本地,此时下载图片属于耗时且需要联网的操作所以不能在ui线程中实现,我们创建异步任务AsyncTask完成图片下载,并通知UI线程更新进度条界面。下载完成应该返回图片的保存地址准备将数据写入数据库中 由于要展示下载的进度我们利用接口回调比较好实现。下面是下载图片的异步任务代码:

    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.HttpURLConnection;
    import java.net.URL;
    
    import android.os.AsyncTask;
    import android.os.Environment;
    
    public class MyTask extends AsyncTask<String, Integer, String> {
    
        public interface CallBack {
            public void start(); //主界面展示一个进度条
    
            public void updataProgress(int progress); //更新进度条
    
            public void finish(String imgPath); //下载完成返回文件保存到绝对路径
        }
    
        CallBack cb;
    
        public MyTask(CallBack cb) {
            super();
            this.cb = cb;
        }
    
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            if (cb != null) {
                cb.start(); //准备工作
            }
        }
    
        @Override
        protected String doInBackground(String... params) {
            // 1.HttpURLConnection
            HttpURLConnection conn = null;
            String imgPath = null;
            // 2.URL
            try {
                URL url = new URL(params[0]);
                // 3.url.openConnection
                conn = (HttpURLConnection) url.openConnection();
                // 4.InputStream
                InputStream in = conn.getInputStream();
                // 获取该文件的总长度
                int total = conn.getContentLength();
                // 5.获取保存文件的路径及文件 名/sdcard/image
                String path_sdcard = Environment.getExternalStorageDirectory()
                        .getAbsolutePath() + "/image";
                File fileParent = new File(path_sdcard);
                // 判断该目录是否存在,如果不存在,创建该目录
                if (!fileParent.exists()) {
                    // 创建目录
                    fileParent.mkdirs();
                }
                String arr[] = params[0].split("/");
                String filenameString = arr[arr.length - 1];
                // 6.创建File对象,再拿到OutputStream
                File file = new File(path_sdcard, filenameString);
                if (file.exists()) {
                    return file.getAbsolutePath();
                }
                // 用来返回该img路径
                imgPath = file.getAbsolutePath();
                OutputStream out = new FileOutputStream(file);
    
                byte[] buffer = new byte[4096];
                int sum = 0;
                int len = 0;
                while ((len = in.read(buffer)) != -1) {
                    out.write(buffer, 0, len);
                    // 累加
                    sum = sum + len;
                    // 计算百分比
                    int per = (int) (sum * 100f / total);
                    // 发布进度值
                    publishProgress(per);
                }
                out.flush();
                out.close();
                in.close();
                // 返回当前被保存的img的绝对路径
                return imgPath;
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } finally {
                if (conn != null) {
                    conn.disconnect();
                }
            }
            //下载异常返回NULL
            return null;
        }
    
        @Override
        protected void onProgressUpdate(Integer... values) {
            // TODO Auto-generated method stub
            super.onProgressUpdate(values);
            if (cb != null) {
                cb.updataProgress(values[0]); //更新进度条
            }
        }
    
        /*
         * result表示的是图片所在的的路径
         */
        @Override
        protected void onPostExecute(String result) {
            // TODO Auto-generated method stub
            super.onPostExecute(result);
            if (cb != null) {
                cb.finish(result); //返回图片地址
            }
        }
    
    }
    

    图片下载完成并且保存到本地了 此时我们应该将图片名称 URL 绝对路径 写入数据库。 那么此时我们应该开始创建数据库了,自定义MySqliteHelper 继承SQLiteOpenHelper 就好了,代码如下

    import android.content.Context;
    import android.database.sqlite.SQLiteDatabase;
    import android.database.sqlite.SQLiteOpenHelper;
    import android.util.Log;
    
    public class MySqliteHelper extends SQLiteOpenHelper {
        public MySqliteHelper(Context context) {
            super(context, "picture.db", null, 1);
        }
    
        @Override
        public void onCreate(SQLiteDatabase db) {
            // 创表
            String sql = "create table img(_id integer primary key autoincrement,name text ,url text,path text)";
            db.execSQL(sql);
            Log.d("onCreateDataBase", "helper onCreate create table img");
    
    
        }
    
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            // TODO Auto-generated method stub
    
        }
    
    }
    

    数据库有着落了,那么我们应该想着如何将数据展示到listview中,这里ListView中的每个item包含了一张图片一个文本 。 这里就用就灵活的BaseAdapter完成,我们自定义一个MyAdapter继承自BaseAdapter。

    import java.io.File;
    import java.util.ArrayList;
    import android.content.Context;
    import android.database.sqlite.SQLiteDatabase;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.BaseAdapter;
    import android.widget.ImageView;
    import android.widget.TextView;
    import android.widget.Toast;
    
    public class MyAdapter extends BaseAdapter {
        ArrayList<Picture> data;
        Context context;
        LayoutInflater inflater;
        int progress;
    
        public MyAdapter(ArrayList<Picture> data, Context context) {
            super();
            this.data = data;
            this.context = context;
            inflater = LayoutInflater.from(context);
        }
    
        @Override
        public int getCount() {
            return data.size();
        }
    
        @Override
        public Object getItem(int position) {
            return data.get(position);
        }
    
        @Override
        public long getItemId(int position) {
            return 0;
        }
    
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            // TODO Auto-generated method stub
            ViewHolder holder = null;
    
            if (convertView == null) {
                holder = new ViewHolder();
                convertView = inflater.inflate(R.layout.list_item, null);
                holder.tv_1 = (TextView) convertView.findViewById(R.id.tv);
                holder.iv = (ImageView) convertView.findViewById(R.id.img);
                convertView.setTag(holder);
    
            } else {
                holder = (ViewHolder) convertView.getTag();
            }
    
            Picture picture = data.get(position);
            File file = new File(picture.path);
            if (file.exists()) { // 如果sd卡的图片存在去设置图片
                holder.tv_1.setText(picture.name);
                Bitmap bitmap = BitmapFactory.decodeFile(picture.path);
                holder.iv.setImageBitmap(bitmap);
            } else { // 图片不存在提示用户
                Toast.makeText(context, picture.name + "好像出了点问题,图片是否被你删除?",
                        Toast.LENGTH_SHORT).show();
                // 本地图片被删除 删除数据库中的数据
                MySqliteHelper helper = new MySqliteHelper(context);
                SQLiteDatabase db = helper.getWritableDatabase();
                String sql = "DELETE FROM img WHERE path = '" + picture.path + "'";
                db.execSQL(sql);
    
            }
    
            return convertView;
        }
    
        class ViewHolder {
            TextView tv_1;
            ImageView iv;
    
        }
    
    }

    数据展示问题也解决了,那么 接下来就是删除事件,点击listview 或者长按listview中的item都有对应的监听事件分别是setOnItemClickListener、 setOnItemLongClickListener
    删除事件的实现:

    lv.setOnItemLongClickListener(new OnItemLongClickListener() {
    
                @Override
                public boolean onItemLongClick(AdapterView<?> parent, View view,
                        final int position, long id) {
                    AlertDialog.Builder builder = new AlertDialog.Builder(
                            MainActivity.this);
    
                    builder.setTitle("刪除" + data.get(position).name).setMessage(
                            "此操作不可逆,是否继续?");
                    // 相当于确定
                    builder.setPositiveButton("确定",
                            new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog,
                                        int which) {
    
                                    /*
                                     * 注释部分为普通sql查询语句 String sql =
                                     * "delete from img where url='" +
                                     * data.get(position).url + "'";
                                     * db.execSQL(sql);
                                     */
    
                                    // 1.获取连接服务地址
                                    Uri uri = Uri
                                            .parse("content://com.picture.provider");
                                    // 2.获取ContentResolver
                                    ContentResolver cr = getContentResolver();
                                    // 3.根据位置获取到数据库中相应的url 作为条件删除相应的数据
                                    cr.delete(uri, "url='" + data.get(position).url
                                            + "'", null);
    
                                    File file = new File(data.get(position).path);
                                    if (file.exists()) { // 如果存在那么删除本地文件
                                        file.delete();
                                    }
                                    data = readDataBase(); // 读取数据库中的内容
    
                                    MainActivity.this.position = -1;// 每次删除完毕设置当前位置为-1 为下一次点击做准备
    
                                    adapter.notifyDataSetChanged();// 提示更新界面
    
                                }
                            });
                    // 相当于取消 这里啥都不干
                    builder.setNegativeButton("取消",
                            new DialogInterface.OnClickListener() {
    
                                @Override
                                public void onClick(DialogInterface dialog,
                                        int which) {
    
                                }
                            });
    
                    builder.show();
    
                    return false;
                }
            });

    public void del(View view) {// 删除图片
            if (position != -1) {
                AlertDialog.Builder builder = new AlertDialog.Builder(
                        MainActivity.this);
    
                builder.setTitle("刪除" + data.get(position).name).setMessage(
                        "此操作不可逆,是否继续?");
                // 相当于确定
                builder.setPositiveButton("确定",
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                /*
                                 * String sql = "delete from img where url='" +
                                 * data.get(position).url + "'"; db.execSQL(sql);
                                 */
                                Uri uri = Uri
                                        .parse("content://com.picture.provider");
                                // 获取ContentResolver
                                ContentResolver cr = getContentResolver();
                                // 准备数据
                                cr.delete(uri, "url='" + data.get(position).url
                                        + "'", null);
    
                                File file = new File(data.get(position).path);
                                if (file.exists()) { // 如果存在那么删除本地文件
                                    file.delete();
                                }
                                data = readDataBase(); // 读取数据库中的内容
                                if (data.size() == 0) { // 没有东西那么接下的链接可以直接下载
                                    flag = true;
                                    MainActivity.this.position = -1;
                                }
                                adapter.notifyDataSetChanged();// 提示更新界面
                            }
                        });
                // 相当于取消
                builder.setNegativeButton("取消",
                        new DialogInterface.OnClickListener() {
    
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
    
                            }
                        });
    
                builder.show();
            } else {
                Toast.makeText(this, "当前没有选中任何图片!", Toast.LENGTH_LONG).show();
            }
    
        }

    删除功能也实现了,接下来我们实现ContentProvider功能其实也简单,写一个类继承自ContentProvider

    import android.content.ContentProvider;
    import android.content.ContentUris;
    import android.content.ContentValues;
    import android.database.Cursor;
    import android.database.sqlite.SQLiteDatabase;
    import android.net.Uri;
    import android.util.Log;
    
    public class MyContentProvider extends ContentProvider {
        MySqliteHelper helper;
    
        @Override
        public boolean onCreate() {
            helper = new MySqliteHelper(getContext());
            if (helper != null) {
                return true;
            }
            return false;
        }
    
        @Override
        public Cursor query(Uri uri, String[] projection, String selection,
                String[] selectionArgs, String sortOrder) {
            SQLiteDatabase db = helper.getWritableDatabase();
    
            Cursor cursor = db.query("img", projection, selection, selectionArgs,
                    null, null, sortOrder);
            cursor.setNotificationUri(getContext().getContentResolver(), uri); // 通知界面更新
            return cursor;
        }
    
        @Override
        public String getType(Uri uri) {
    
            return null;
        }
    
        @Override
        public Uri insert(Uri uri, ContentValues values) {
            Log.e("insert", " " + uri.getAuthority());
            SQLiteDatabase db = helper.getWritableDatabase();
            long id = -1;
            id = db.insert("img", null, values);
            db.close();
            getContext().getContentResolver().notifyChange(uri, null);
            return ContentUris.withAppendedId(uri, id); 
        }
    
        @Override
        public int delete(Uri uri, String selection, String[] selectionArgs) {
            Log.e("delete", "delete");
            SQLiteDatabase db = helper.getWritableDatabase();
            int count = db.delete("img", selection, selectionArgs);
            getContext().getContentResolver().notifyChange(uri, null);
            return count; //返回删除的条数
        }
    
        @Override
        public int update(Uri uri, ContentValues values, String selection,
                String[] selectionArgs) {
            Log.e("update", "update"+selection);
            SQLiteDatabase db = helper.getWritableDatabase();
            int count = db.update("img", values, selection, selectionArgs);
            getContext().getContentResolver().notifyChange(uri, null);
            return count;
        }
    
    }
    

    在xml中注册provider提供给其他程序

       <provider
                android:name="com.wenxiangli.MyContentProvider"
                android:authorities="com.picture.provider"
                android:exported="true" >
        </provider>

    至此我们需要的功能都实现了。接下来说几个遇到的问题注意点:

    1.权限一定不要忘记了

     <!-- 联网权限 -->
        <uses-permission android:name="android.permission.INTERNET" />
        <!-- SDcard的读写权限 -->
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
        <!-- 读取Sdcard状态权限 -->
        <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />

    2.为了完成第二次启动加载本地图片,我们应该先从数据库读出数据然后绑定到集合中去通知adapter更新
    3.删除特别注意越界问题,这里我是通过每次删除结束设置positon为-1,当点击事件产生将改变position的值去判断是否删除。
    4.保证图库中的数据唯一,所以每次下载前判断图片是否存在,存在就不去下载。直接提示是否更改文件的名字
    这里写图片描述
    这里写图片描述


    下面附上完整源码地址需要的可以在这下载
    链接:http://pan.baidu.com/s/1dEW5JBr 密码:m3jb

    写这么长不容易啊,如果对你有帮助赏给回复 啊哈哈

    展开全文
  • 香港--(美国商业资讯)--香港:K11 ARTUS 继万众期待的香港瑰丽酒店在艺术与设计地标Victoria Dockside开业之后,企业家...K11 ARTUS充分展现艺术融入生活的概念,公共空间陈列着与郑先生的新文化项目K11 Craft &am...

    香港--(美国商业资讯)--香港:K11 ARTUS

    继万众期待的香港瑰丽酒店在艺术与设计地标Victoria Dockside开业之后,企业家郑志刚将在今年第三季度推出K11 ARTUS。作为郑先生的K11集团旗下首个豪华租赁公寓,K11 ARTUS肩负着保护濒临失传的传统工艺的社会使命。K11 ARTUS充分展现艺术融入生活的概念,公共空间陈列着与郑先生的新文化项目K11 Craft & Guild Foundation工艺文化基金会共同创造的作品,该项目旨在保护和传承自明清时代的工艺技术。公共空间还陈放了专为K11 ARTUS打造、并可对外出售的稀有中式艺术作品。销售所得收益将用于支持中国工匠艺人。这些作品包括使用古老百宝嵌技术(将雕刻物品与宝石镶嵌)制作的木质家具、以及广彩瓷器(糅合了东西方元素的色彩绚丽手绘瓷器)等。此外,K11 ARTUS还将在全年内定期邀请重量级文化界人士举办沙龙活动,为公寓增添独特的人文元素。同时,设立专用的体验空间展示中国最优秀的工艺遗产。在建筑方面, 287套的公寓住所其中284套均由傅厚民André Fu设计,住客从蜿蜒的阳台上可俯瞰令人叹为观止的维多利亚海港景色。不仅如此,K11 ARTUS还拥有3间分别由Joyce Wang、Fiona Barratt-Campell和nemaworkshop设计的奢华顶层海景复式花园公寓。

    孟买:SOHO HOUSE MUMBAI

    尽管今年早些时候新开业的Soho House Mumbai是Soho House家族的第23位成员,但已在家族中備受各大公司的青睐,同时也是孟买上流人士的最爱。Soho House Mumbai有着像孟买一样绚丽的色彩,这个集俱乐部与酒店于一身的混合体装饰有各种图案和织物,其中很多家具和物件都来自于Soho House设计团队在新德里、斋普尔,当然还有孟买之行中发现的工匠。定制的家具包括藤椅、雕板印刷的窗帘以及由复古式沙丽制作的绝美灯罩,这些都成为每个房间的焦点。在放映厅,扶手椅使用了马海毛,而拉贾斯坦邦手工印制的织物则有助于隔音。当然,Soho House常见的一切事物都可以在这里找到,不论是Cecconi分店或令人印象深刻的艺术藏品,包括Bharti Kher和Subodh Gupta的作品。

    清迈:RAYA HERITAGE

    作为清迈的精品酒店,Raya Heritage的设计灵感来自于13世纪兰娜王国,其充满艺术气息的家具涵盖柚木镜子和陶瓷地砖,以及采购自当地编织公司、用当地树木色素印染的织物。这家全套房酒店是曼谷建筑师Boonlert Hemvijitraphan的杰作,仅有38间套房,俯瞰湄滨河。虽然酒店的设施都是最先进的,但其结构和材质依然充斥着手工元素,例如,招牌餐厅以Khu Khao為名,名称取自于泰国北部稻农所使用的脱谷篮,而这些手工编织的篮子则被融入了酒店的吊灯装饰。在房间里,篮子、木质雕刻和陶器均出自当地周边村落的工匠之手。

    伦敦:THE CONDUIT

    The Conduit并非只是一个供人闲聊的会员俱乐部,它汇集了一批志同道合而且拥有不菲身家和广泛人脉的人士。除了致力于提升社会意识,同时让成员积极参加慈善活动和采取负责任的生活方式(毕竟创始成员包括Christiane Amanpour和Amnesty International的Salil Shetty)。其设计理念十分有趣,既融合了斯堪的纳维亚美学,又吸纳了非洲工艺美术,由Russell Sage Studio与Cavendish Studio这样的能人打造,当然也得到了联合创始人Paul Van Zyl的指引。Paul是一名人权律师,创业经历包括颇具艺术气息的奢华时装品牌Maiyet。The Conduit是一家有社会良知的俱乐部,其家具和物件均采购于非洲非营利机构和工艺群体,从斯威士兰妇女团体编织的安哥拉挂毯,到来自南非比勒陀利亚马摩洛迪的瓷器,这些机构和群体制作的产品均被用于造福社会。

    福鼎:小溪家

    这个带有客房的改造项目显然不是以突显奢华为目的,而且基本上没有任何多余的家具,但其构造毫无疑问彰显了实用、可转移的美学工艺。“小溪家”坐落于一个饱受贫困折磨的村落——赤溪村,是北京建筑师事务所WEI Architects的展示项目。该公司重新装修了一个废弃的房子,将其转化为一间旅馆,展现了村庄如何通过翻新建筑来赚取收入。公司采用了当地材料,并聘请当地建筑工人使用原始传统的建造方式来搭建整体构架。同时,该项目还采用了榫孔及榫舌等传统木质结构,以及特殊的转换式门窗框架,并且重新恢复使用了许多之前已经被废弃的建筑。最终,一个婀娜多姿、在建筑领域极具震撼力的宾客之家应运而生,这建筑模式更可完全应用在其他村落。

    作者:Christina Ko

    香港自由撰稿人兼编辑Christina报导奢华生活已有十多年。她撰写的作品主题涵盖美容健康到艺术文化,她曾担任奢华杂志Prestige Hong Kong的编辑总监,现时为包括Hong Kong TatlerSCMPDiscoverySilverkris等各大报刊撰稿,并与国际品牌包括路易威登、迪奥、雅诗兰黛和连卡佛等客户紧密合作。

    展开全文
  • #grafana 是一款采用 go 语言编写的开源应用,主要用于大规模指标数据的可视化展现,是网络架构应用分析中最流行的时序数据展示工具,目前已经支持绝大部分常用的时序数据库。官网英文链接:...

    Grafana使用mysql作为数据源图文详解

    #grafana 是一款采用 go 语言编写的开源应用,主要用于大规模指标数据的可视化展现,是网络架构和应用分析中最流行的时序数据展示工具,目前已经支持绝大部分常用的时序数据库。官网英文链接:https://grafana.com/docs/grafana/latest/。从官网和网上对mysql作为数据源的中文详细说明很少,笔者结合最近的项目,详细介绍mysql作为数据源的详细配置和技巧。

    一,连接数据源
    在这里插入图片描述
    在这里插入图片描述
    参数说明:

    host:主机名或IP地址及端口号

    database:数据库名字

    user:访问数据库的用户名

    password:用户的密码

    Max open:可使用最大连接数

    Max idle:最大连接池连接数,默认值为2

    从连接数据库processlist可以看到,grafana使用2个连接维持grafana和数据库的连接。

    在这里插入图片描述
    这里需要注意grafana并没有对查询语句进行安全性验证,所以我们最好需要创建一个用户并设置只读权限来保证数据库访问的安全性。例如:

    CREATE USER ‘grafanaReader’ IDENTIFIED BY ‘password’;

    GRANT SELECT ON mydatabase.mytable TO ‘grafanaReader’;

    二,创建dashboard和panel
    在这里插入图片描述

    在这里插入图片描述
    grafana提供很多可视化panel单元,如图形、表格等,每个panel有一个查询编辑器配置数据提供给前端展示。在同一个dashboard中不同的panel是可以按拖拽方式排列放大或缩小。

    三,查询和panel配置

    以graph为例详细介绍一下查询器和panel配置。
    在这里插入图片描述
    graph分为三部分,右边为panel设置选项,包括panel名字和描述、显示属性、X与Y轴刻度单位、旋停配置等信息;下面为query设置选项,具体参数与我们选择的数据源有关系,提供了自动编译SQL语句、度量名字和宏等信息;上面为最终展示视图,将我们Query设置的查询数据和配置信息显示到该视图上面。​

    在query设置选项中,grafana的查询编辑器会自动选取具有时间戳或者日期的字段。

    FROM字段可以选择配置数据库中的表也可以选择其他的数据库的表,但是需要手动指定数据库名字.表名的形式。

    1,Time Column字段选择具有时间属性的字段进行分组,在我的项目中选择created_time作为时间进行分组,时间字段转换成UNIX时间戳按分钟进行分组。

    例如:

    SELECT

    UNIX_TIMESTAMP(created_time) DIV 60 * 60 AS “time”,

    avg(pjfs) AS “pjfs”

    FROM test

    WHERE

    created_time BETWEEN FROM_UNIXTIME(1605024000) AND FROM_UNIXTIME(1605110399)

    GROUP BY 1

    ORDER BY UNIX_TIMESTAMP(created_time) DIV 60 * 60

    2,Metric column字段是可选字段,其含义为监控单元项,建议使用数据库表中的文本类型的字段。如果是其他类型字段,可以使用cast函数进行转换。

    例如:

    我们选择fcbm作为metric column,那么SQL语句就变为,

    SELECT

    UNIX_TIMESTAMP(created_time) DIV 60 * 60 AS “time”,

    fcbm AS metric,

    avg(pjfs) AS “pjfs”

    FROM test

    WHERE

    created_time BETWEEN FROM_UNIXTIME(1605024000) AND FROM_UNIXTIME(1605110399)

    GROUP BY 1,2

    ORDER BY UNIX_TIMESTAMP(created_time) DIV 60 * 60

    将created_time和fcbm作为联合分组。

    3,SELECT字段用于指定我们想要选择数据库表中的字段作为我们展示数据,这些字段可以使用表达式或在聚合函数。

    在上例子中 ,

    avg(pjfs) AS “pjfs”

    我们使用avg均值聚合函数对pjfs求一分钟内的平均值。

    4,WHERE字段用于指定选择条件或在过滤一些记录。

    5,Group by 字段用于分组,query编辑器会自动将select的字段进行分组,如果需要可以手动添加用于分组的字段,grafana要求对于分组的字段都应该有聚合函数,如果没有将会自动添加上。

    6,Gap Filling

    当选择时间戳分组后,如果有缺失的数值,我们可以选择time函数处理缺失的数据,该函数有两个参数,第一个参数用于时间分组的窗口;第二个参数用于缺失值处理方式(null,0,previous)
    在这里插入图片描述
    配置完Query编辑器后,整个展示效果:
    在这里插入图片描述

    展开全文
  • grafana 是一款采用 go 语言编写的开源应用,主要用于大规模指标数据的可视化展现,是网络架构应用分析中最流行的时序数据展示工具,目前已经支持绝大部分常用的时序数据库。官网英文链接:...

    grafana 是一款采用 go 语言编写的开源应用,主要用于大规模指标数据的可视化展现,是网络架构和应用分析中最流行的时序数据展示工具,目前已经支持绝大部分常用的时序数据库。官网英文链接:https://grafana.com/docs/grafana/latest/。从官网和网上对mysql作为数据源的中文详细说明很少,笔者结合最近的项目,详细介绍mysql作为数据源的详细配置和技巧。

    一,连接数据源

    d88e8c0cef78191973a99a228d54df12.png

    参数说明:

    host:主机名或IP地址及端口号

    database:数据库名字

    user:访问数据库的用户名

    password:用户的密码

    Max open:可使用最大连接数

    Max idle:最大连接池连接数,默认值为2

    从连接数据库processlist可以看到,grafana使用2个连接维持grafana和数据库的连接。

    10fee9c05bea3736bb5f035f2027ca76.png

    这里需要注意grafana并没有对查询语句进行安全性验证,所以我们最好需要创建一个用户并设置只读权限来保证数据库访问的安全性。例如:34Z

     CREATE USER 'grafanaReader' IDENTIFIED BY 'password';

     GRANT SELECT ON mydatabase.mytable TO 'grafanaReader';

    二,创建dashboard和panel

    6fce17bd7cb91e72facb61dfcae8799c.png

    a106085319ae6aea18d8668c33716638.pnggrafana提供很多可视化panel单元,如图形、表格等,每个panel有一个查询编辑器配置数据提供给前端展示。在同一个dashboard中不同的panel是可以按拖拽方式排列放大或缩小。

    三,查询和panel配置

    以graph为例详细介绍一下查询器和panel配置。

    eb46497b5b182cfd57ea8b3f7c3de6a5.png

    graph分为三部分,右边为panel设置选项,包括panel名字和描述、显示属性、X与Y轴刻度单位、旋停配置等信息;下面为query设置选项,具体参数与我们选择的数据源有关系,提供了自动编译SQL语句、度量名字和宏等信息;上面为最终展示视图,将我们Query设置的查询数据和配置信息显示到该视图上面。

    在query设置选项中,grafana的查询编辑器会自动选取具有时间戳或者日期的字段。

    FROM字段可以选择配置数据库中的表也可以选择其他的数据库的表,但是需要手动指定数据库名字.表名的形式。

    1,Time Column字段选择具有时间属性的字段进行分组,在我的项目中选择created_time作为时间进行分组,时间字段转换成UNIX时间戳按分钟进行分组。

    例如:

    SELECT
    UNIX_TIMESTAMP(created_time) DIV 60 * 60 AS "time",
    avg(pjfs) AS "pjfs"
    FROM test
    WHERE
    created_time BETWEEN FROM_UNIXTIME(1605024000) AND FROM_UNIXTIME(1605110399)
    GROUP BY 1

    ORDER BY UNIX_TIMESTAMP(created_time) DIV 60 * 60

    2,Metric column字段是可选字段,其含义为监控单元项,建议使用数据库表中的文本类型的字段。如果是其他类型字段,可以使用cast函数进行转换。

    例如:

    我们选择fcbm作为metric column,那么SQL语句就变为,

    SELECT
    UNIX_TIMESTAMP(created_time) DIV 60 * 60 AS "time",
    fcbm AS metric,
    avg(pjfs) AS "pjfs"
    FROM test
    WHERE
    created_time BETWEEN FROM_UNIXTIME(1605024000) AND FROM_UNIXTIME(1605110399)GROUP BY 1,2

    ORDER BY UNIX_TIMESTAMP(created_time) DIV 60 * 60

    将created_time和fcbm作为联合分组。

    3,SELECT字段用于指定我们想要选择数据库表中的字段作为我们展示数据,这些字段可以使用表达式或在聚合函数。

    在上例子中 ,

    avg(pjfs) AS "pjfs"

    我们使用avg均值聚合函数对pjfs求一分钟内的平均值。

    4,WHERE字段用于指定选择条件或在过滤一些记录。

    5,Group by 字段用于分组,query编辑器会自动将select的字段进行分组,如果需要可以手动添加用于分组的字段,grafana要求对于分组的字段都应该有聚合函数,如果没有将会自动添加上。

    6,Gap Filling

    当选择时间戳分组后,如果有缺失的数值,我们可以选择time函数处理缺失的数据,该函数有两个参数,第一个参数用于时间分组的窗口;第二个参数用于缺失值处理方式(null,0,previous)

    e77a1292b6a08bc904a42ff5fd632134.png

    配置完Query编辑器后,整个展示效果:

    b4942c7193e674089a2756dc93fe6059.png

    展开全文
  • grafana 是一款采用 go 语言编写的开源应用,主要用于大规模指标数据的可视化展现,是网络架构应用分析中最流行的时序数据展示工具,目前已经支持绝大部分常用的时序数据库。官网英文链接:...
  • grafana 是一款采用 go 语言编写的开源应用,主要用于大规模指标数据的可视化展现,是网络架构应用分析中最流行的时序数据展示工具,目前已经支持绝大部分常用的时序数据库。官网英文链接:...
  • 更有效的个人能力展现 ... 我的档案 来查看自己的档案信息了。 当前版本的个人档案,可以展示你的基本信息在 SegmentFault 社区的主要指标,包括擅长的标签高票答案;...个人档案将作为你的自我信息展示,在 Segme...
  • 面临新时期、新任务和新的考验,作为一名共产党员,只有不断地充实、完善提高自己的综合素质,才能担当得起历史赋于我们的重任,才能用自身行动向人发群众来展示党的先进性。 一、牢记远大理想,坚定共产主义信念...
  • 在之前delphi.net开发中,要展示网格数据一般都有现成的Grid控件,而在android中,则可以用Listview展现网格数据,下面是几种方式效果图: 第一种效果图: 嵌入式Listview:就是说定义一个xml的layout作为...
  • 作为施工演示动画的一种类型,flash施工动画制作应该展现哪些内容呢?接下来艺虎动画一起了解。flash施工动画制作展现的内容应包括两个方面,其一便是技术展示,即对工程设计进行介绍。动画公司根据工程设...
  • 在前面的随笔中,已经介绍了ABP的增删改查的操作,但是对于查询的数据并没有进行分页,只是进行粗糙的展示,今天的随笔中将摸索进行分页展示。这里打算使用的分页插件是DataTables,...分页作为一个页面展示的基础功...
  • 有着非常漂亮的图表布局展示,功能齐全的度量仪表盘图形编辑器 支持Graphite、zabbix、InfluxDB、Prometheus OpenTSDB 作为数据源 最新版本4.3.1已经支持 MySQL 数据源。 Grafana 主要特性: 灵活丰富的...
  • 大家周末好,作为小菜鸟的我,在上周的工作中遇到一个问题.在师傅的指点下解决了.现在我想总结一下,一来是巩固知识点,二来是分享给我有通用困惑的小小伙伴;问题其实很简单,各位大神勿喷!!!一.项目需求 在今天工作...
  • 崇左市警体馆_展示展馆设计_如何打造一个展馆湖南美景创意文化建设有限公司,创办于2003年3月,是一家集“策划-设计-生产-施工-”于一体的文化建设与环境...又要因地制宜,深入挖掘展现具有浓郁地方特色民族特色...
  • OpenLayer是一个用于浏览器地图展示的js库,用于构建基于网络的地理应用,OpenLayer使用Prototype.jsRico作为核心库的基础,实现了工业标准的地理数据访问方法(OGC标准)。在企业级GIS应用中,能较好的作为客户端...
  • 中金数据科博会展示云计算成果

    千次阅读 2012-05-28 14:59:47
    5月27日,第十五届北京国际科技产业博览会(以下简称科博会)顺利落下帷幕,本届科博会受到海内外政府、科 ... 作为国内领先的云计算运营服务商信息系统外包服务专业提供商,中金数据系统有限公司参展本
  • 作为存储与网络技术创新的推动者,Hifn:trade_mark:(NASDAQ:HIFN)将在第136号展位对外展示统一存储平台,重点展现其如何将多功能企业级存储应用程序安装在单一的高性能存储系统中。统一存储平台演示代表了Hifn为...
  • 来自不同行业、不同性别、不同年龄段的藏家,将不同历史阶段、不同国别、不同内容的钱币进行统一梳理集中展示,讲述钱币背后的故事。钱币展示。 杨杰英 摄太原海归俱乐部主席范锐作为当地钱币收藏圈的名人,对收藏...
  • 6月1日,由开放网络基金会(ONF)、天地互连-全球SDN测试认证中心(SDNCTC) 联合Ixia共同发起的...Fest测试论坛将以论坛演讲介绍、现场演示还原Showcase综合展示,立体式呈现了5月16日-20日成功举办的SDNFV Fe...
  • 展示广告的实时竞价算法浅析

    千次阅读 2014-12-27 22:55:33
    在传统媒体的广告投放平台中,用户广告主都是被动的:一旦用户接触到媒体,广告就会展现出来。 而在实时竞价的广告体系,广告主会通过第三方平台(DSP)的协助合理地评估每个用户在当前广告位曝光某个商品所产生的...
  • “财大气粗”的海尔依旧铺设了一个很大的展台,带着自己旗下众多的智能家居产品集体亮相,向在场观众展示自己“U+智慧生活3.0”。 作为一个传统家电制造商,这也就意味着海尔在智能家居的先天优势。在展会现场...
  • 我们知道面试官常常会出一些题让你来解决,作为一名程序员,除了需要具备解决问题的思路以外,代码的质量简洁性也很关键。因为从一个人的代码可以直接看出你的基本功。对于Python而言,这就意味着你需要...
  • 我们知道面试官常常会出一些题让你来解决,作为一名程序员,除了需要具备解决问题的思路以外,代码的质量简洁性也很关键。因为从一个人的代码可以直接看出你的基本功。对于Python而言,这就意味着你需要对Python的...
  • 借助新兴的信息技术能够随时随地感知、捕获、传递处理信息,能够实现对城市的精细化、智能化管理,完善公共服务设施城市功能,创造安全、便捷、高效清洁环保的环境,让人们能够在智慧城市中更好生活。...
  • 第二部分将会介绍webpack内部插件与钩子关系的可视化展示工具📈,用一张图理清webpack内部这种错综复杂的关系。 <p><strong>可视化工具使用效果图:</strong></p> <p><img alt="" src=...
  • 通过良好的、友善的可视化界面能提高感知实用度,因此我这里继续使用tkinter库作为前端的展示开发库,在这里,我将使用ttk的Progressbar实现在统计过程中的等待交互窗口、使用notebook进行分TAB页显示,使用...

空空如也

空空如也

1 2 3 4 5 ... 17
收藏数 324
精华内容 129
关键字:

展现作为和展示作为