• Cocos2d-x 3.x 头像选择,本地相册图片+图片编辑(Android、IOS双平台)


    大连游戏产业不是很发达,最后,选择一个应用程序外包公司。积累的工作和学习过程中的一点业余生活微信体验,我想分享的游戏小朋友的爱。

    在应用开发过程中会经常实用户上传头像的功能,在网上找了N多资料发现没有人详细介绍过该用cocos2d-x实现。这篇文章就来介绍一下怎样在Android和IOS平台上实现该功能。


    先传一张完毕后的图片一饱眼福安静:= = 怎么不好用呢~


    直接上代码:

    头文件 ImagePicker.h

    /**************************************************************************
     * Copyright (c) 2015, pxbomb, All rights reserved.
     
     * File		: ImagePicker.h
     * Date		: 2015/06/02 18:02
     * Author	: 田伟汉
     * Email	: wilhan.tian@gmail.com
     * Depict	: 图像选择器
     **************************************************************************/
    #ifndef _IMAGEPICKER_H_
    #define _IMAGEPICKER_H_
    
    #include "cocos2d.h"
    
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
    #include "platform/android/jni/JniHelper.h"
    #include <jni.h>
    #endif	// CC_PLATFORM_ANDROID
    
    /**
     * 图像选择器
     */
    class ImagePicker
    {
    public:
        // 获取选择器单例
        static ImagePicker* getInstance();
    
        // 销毁
        static void destoryInstance();
    public:
        // 显示本地相冊与相机选择器
        void callImagePickerWithPhotoAndCamera(const std::function<void(std::string)>& callback);
        
        // 设置监听
        void setListener(const std::function<void(std::string)>& callback);
        
        // 移除监听
        void removeListener();
        
        // 打开相冊
        void openPhoto();
        
        // 打开相机
        void openCamera();
        
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
        // 设置AppController
        void setViewController(void* viewController);
    #endif // CC_PLATFORM_IOS
    
    protected:
        // 初始化
        bool init();
        
        ImagePicker();
        
    protected:
        std::function<void(std::string)> m_callback;
        static ImagePicker* s_instance;
        
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
        void* m_viewController;
    #endif // CC_PLATFORM_IOS
        
    };
    
    #endif // _IMAGEPICKER_H_


    实现文件 ImagePicker.cpp

    #include "ImagePicker.h"
    //--------------------------------------------------
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
    #import  "ImagePickerViewController.h"
    #import  "RootViewController.h"
    #endif
    //--------------------------------------------------
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
    #define JAVA_CLASS              "org/cocos2dx/cpp/ImagePicker"
    #define JAVA_FUNC_OPEN_PHOTO    "openPhoto"
    #define JAVA_FUNC_OPEN_CAMERA   "openCamera"
    #endif
    //--------------------------------------------------
    USING_NS_CC;
    //--------------------------------------------------
    ImagePicker* ImagePicker::s_instance = NULL;
    //--------------------------------------------------
    ImagePicker* ImagePicker::getInstance()
    {
        if (s_instance == NULL)
        {
            s_instance = new ImagePicker();
        }
        return s_instance;
    }
    //--------------------------------------------------
    void ImagePicker::destoryInstance()
    {
        CC_SAFE_DELETE(s_instance);
    }
    //--------------------------------------------------
    ImagePicker::ImagePicker()
    :m_callback(nullptr)
    {
        Director::getInstance()->getEventDispatcher()->addCustomEventListener("ImagePickerEvent", [=](EventCustom* eve)
        {
            std::string* path = (std::string*)eve->getUserData();
            if (path && m_callback != nullptr)
            {
                m_callback(*path);
            }
        });
    }
    //--------------------------------------------------
    void ImagePicker::callImagePickerWithPhotoAndCamera(const std::function<void(std::string)>& callback)
    {
        s_instance->init();
        setListener(callback);
    }
    //--------------------------------------------------
    void ImagePicker::setListener(const std::function<void(std::string)>& callback)
    {
        m_callback = callback;
    }
    //--------------------------------------------------
    void ImagePicker::removeListener()
    {
        m_callback = nullptr;
    }
    //--------------------------------------------------
    void ImagePicker::openPhoto()
    {
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
        ImagePickerViewController* imagePickerViewController = [[ImagePickerViewController alloc] initWithNibName:nil bundle:nil];
        
        RootViewController* _viewController = (RootViewController*)m_viewController;
        [_viewController.view addSubview:imagePickerViewController.view];
        
        [imagePickerViewController localPhoto];
    #endif
        
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
        JniMethodInfo info;
        bool ret = JniHelper::getStaticMethodInfo(info, JAVA_CLASS, JAVA_FUNC_OPEN_PHOTO,"()V");
        if (ret)
        {
            info.env->CallStaticVoidMethod(info.classID, info.methodID);
        }
    #endif
    }
    //--------------------------------------------------
    void ImagePicker::openCamera()
    {
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
        ImagePickerViewController* imagePickerViewController = [[ImagePickerViewController alloc] initWithNibName:nil bundle:nil];
        
        RootViewController* _viewController = (RootViewController*)m_viewController;
        [_viewController.view addSubview:imagePickerViewController.view];
        
        [imagePickerViewController takePhoto];
    #endif
        
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
        JniMethodInfo info;
        bool ret = JniHelper::getStaticMethodInfo(info, JAVA_CLASS, JAVA_FUNC_OPEN_CAMERA,"()V");
        if (ret)
        {
            info.env->CallStaticVoidMethod(info.classID, info.methodID);
        }
    #endif
    }
    //--------------------------------------------------
    bool ImagePicker::init()
    {
        cocos2d::Size visibleSize = Director::getInstance()->getVisibleSize();
        
        //-------------------------------------
        // 根层
        //-------------------------------------
        LayerColor* m_layer = LayerColor::create(Color4B(0, 0, 0, 125));
        m_layer->retain();
        //-------------------------------------
        // button背景
        //-------------------------------------
        Sprite* sprite = Sprite::create("ImagePicker/bk.png");
        sprite->setAnchorPoint(Vec2(0.5, 0));
        sprite->setPosition(Vec2(visibleSize.width/2, 0));
        m_layer->addChild(sprite);
        //-------------------------------------
        // button
        //-------------------------------------
        Menu* menu = Menu::create();
        menu->setPosition(Vec2::ZERO);
        m_layer->addChild(menu);
        //-------------------------------------
        MenuItemImage* btnPhoto  = MenuItemImage::create("ImagePicker/ButtonPhoto.png",  "ImagePicker/ButtonPhoto1.png",  [=](Ref* p)
        {
            openPhoto();
        });
        btnPhoto->setAnchorPoint(Vec2(0.5, 1));
        btnPhoto->setPosition(Vec2(visibleSize.width / 2, 280));
        menu->addChild(btnPhoto);
        //-------------------------------------
        MenuItemImage* btnCamera = MenuItemImage::create("ImagePicker/ButtonCamera.png", "ImagePicker/ButtonCamera1.png", [=](Ref* p)
        {
            openCamera();
        });
        btnCamera->setAnchorPoint(Vec2(0.5, 1));
        btnCamera->setPosition(btnPhoto->getPosition() + Vec2(0, -btnPhoto->getContentSize().height));
        menu->addChild(btnCamera);
        //-------------------------------------
        MenuItemImage* btnCancel = MenuItemImage::create("ImagePicker/ButtonCancel.png", "ImagePicker/ButtonCancel1.png", [=](Ref* p)
        {
            float height = sprite->getContentSize().height;
    
            MoveBy* move = MoveBy::create(0.2, Vec2(0, -height));
            sprite->runAction(move);
            menu  ->runAction(move->clone());
    
            Sequence* seq = Sequence::createWithTwoActions(FadeOut::create(0.2), RemoveSelf::create());
            m_layer->runAction(seq);
        });
        btnCancel->setAnchorPoint(Vec2(0.5, 1));
        btnCancel->setPosition(btnCamera->getPosition() + Vec2(0, -btnCamera->getContentSize().height - 20));
        menu->addChild(btnCancel);
        //-------------------------------------
        // 文字
        //-------------------------------------
        Label* textPhoto  = Label::createWithSystemFont("Photo",  "", 24);
        textPhoto->setPosition(btnPhoto->getContentSize() / 2);
        textPhoto->setTextColor(Color4B::BLACK);
        btnPhoto->addChild(textPhoto);
        //-------------------------------------
        Label* textCamera = Label::createWithSystemFont("Camera", "", 24);
        textCamera->setPosition(btnPhoto->getContentSize() / 2);
        textCamera->setTextColor(Color4B::BLACK);
        btnCamera->addChild(textCamera);
        //-------------------------------------
        Label* textCancel = Label::createWithSystemFont("Cancel", "", 24);
        textCancel->setPosition(btnPhoto->getContentSize() / 2);
        textCancel->setTextColor(Color4B::BLACK);
        btnCancel->addChild(textCancel);
        //-------------------------------------
        // 准备显示
        //-------------------------------------
        Director::getInstance()->getRunningScene()->scheduleOnce([=](float time)
        {
            Director::getInstance()->getRunningScene()->addChild(m_layer, INT_MAX);
            m_layer->release();
    
            float height = sprite->getContentSize().height;
    
            sprite->setPositionY(sprite->getPositionY() - height);
            menu  ->setPositionY(menu->getPositionY()   - height);
    
            MoveBy* move = MoveBy::create(0.3, Vec2(0, height));
            sprite->runAction(move);
            menu  ->runAction(move->clone());
    
            m_layer->setOpacity(0);
            m_layer->runAction(FadeTo::create(0.2, 125));
    
        }, 0.1, "ImagePickerScheduleOnce");
        //-------------------------------------
        // 截断事件
        //-------------------------------------
        EventListenerTouchOneByOne* touchEvent = EventListenerTouchOneByOne::create();
        touchEvent->setSwallowTouches(true);
        touchEvent->onTouchBegan = [=](Touch* touch, Event* eve)
        {
            if(sprite->getBoundingBox().containsPoint(touch->getLocation()))
                return true;
            
            float height = sprite->getContentSize().height;
            
            MoveBy* move = MoveBy::create(0.2, Vec2(0, -height));
            sprite->runAction(move);
            menu  ->runAction(move->clone());
            
            Sequence* seq = Sequence::createWithTwoActions(FadeOut::create(0.2), RemoveSelf::create());
            m_layer->runAction(seq);
            
            return true;
        };
        Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(touchEvent, sprite);
        //-------------------------------------
        return true;
    }
    //--------------------------------------------------
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
    void  ImagePicker::setViewController(void* viewController)
    {
        m_viewController = viewController;
    }
    #endif
    //--------------------------------------------------
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
    extern "C"
    {
        void Java_org_cocos2dx_cpp_ImagePicker_onImageSaved(JNIEnv* env, jobject thiz, jstring path)
        {
            std::string strPath = JniHelper::jstring2string(path);
            Director::getInstance()->getEventDispatcher()->dispatchCustomEvent("ImagePickerEvent", &strPath);
        }
    }
    #endif
    //--------------------------------------------------

    为project加入资源:略 

    (图片资源和代码下载地址:点击打开链接)


    分平台实现:

    ------------------------------------------

                       Android

    ------------------------------------------

    1. 打开Eclipse在org.cocos2x.cpp(增加你没改过的话)包名下新建“ImagePicker.java”文件

    ImagePicker.java详细代码例如以下:

    public class ImagePicker{
    	
    <span style="white-space:pre">	</span>public static final int 	NONE = 0;
        public static final int 	PHOTOHRAPH = 1;		// 拍照
        public static final int 	PHOTOZOOM = 2; 		// 缩放
        public static final int 	PHOTORESOULT = 3;	// 结果
        public static final String  IMAGE_UNSPECIFIED = "image/*";
        
        private static ImagePicker instance = null;
        private static Activity    activity = null;
        
        public static native void onImageSaved(String path);
        
        public static ImagePicker getInstance(){
        	if(instance == null){
        		instance = new ImagePicker();
        	}
        	return instance;
        }
        
        // 初始化
        public void init(Activity activity){
        	ImagePicker.activity = activity;
        }
        
        // 打开相冊
    	static public void openPhoto(){
    		Intent intent = new Intent(Intent.ACTION_PICK, null);
            intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, IMAGE_UNSPECIFIED);
            activity.startActivityForResult(intent, PHOTOZOOM);
    	}
    	 
    	// 打开相机
    	static public void openCamera(){
        	Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(activity.getFilesDir(), "@cc_cameraCache.jpg")));
            activity.startActivityForResult(intent, PHOTOHRAPH);
        }
    	
    	// 回调
    	public void onActivityResult(int requestCode, int resultCode, Intent data){
    		if (resultCode == NONE)
                return;
            
            // 拍照
            if (requestCode == PHOTOHRAPH) {
                File picture = new File(activity.getFilesDir() + "/@cc_cameraCache.jpg");
                startPhotoZoom(Uri.fromFile(picture));
            }
     
            if (data == null)
                return;
     
            // 读取相冊缩放图片
            if (requestCode == PHOTOZOOM) {
                startPhotoZoom(data.getData());
            }
            
            // 处理结果
            if (requestCode == PHOTORESOULT) {
                Bundle extras = data.getExtras();
                if (extras != null) {
                    Bitmap photo = extras.getParcelable("data");
                    ByteArrayOutputStream stream = new ByteArrayOutputStream();
                    photo.compress(Bitmap.CompressFormat.JPEG, 75, stream);
                    
                    // XXX/@ci_8888-8888-8888-8888.jpg
                    String path = activity.getFilesDir() + "/@ci_" + UUID.randomUUID().toString() + ".jpg";
                    saveMyBitmap(path, photo);
                    
                    // 通知C++层已保存图片 并返回路径
                    onImageSaved(path);
                }
            }
    	}
    	
    	public void startPhotoZoom(Uri uri) {
            Intent intent = new Intent("com.android.camera.action.CROP");
            intent.setDataAndType(uri, IMAGE_UNSPECIFIED);
            intent.putExtra("crop", "true");
            intent.putExtra("aspectX", 1);
            intent.putExtra("aspectY", 1);
            intent.putExtra("outputX", 100);
            intent.putExtra("outputY", 100);
            intent.putExtra("return-data", true);
            activity.startActivityForResult(intent, PHOTORESOULT);
        }
    	
    	public void saveMyBitmap(String filePath, Bitmap mBitmap){
        	File f = new File(filePath);
        	try {
        		f.createNewFile();
        	} catch (IOException e) {
        	}
        	FileOutputStream fOut = null;
        	try {
        		fOut = new FileOutputStream(f);
        	} catch (FileNotFoundException e) {
        		e.printStackTrace();
        	}
        	mBitmap.compress(Bitmap.CompressFormat.JPEG, 70, fOut);
        	try {
        		fOut.flush();
        	} catch (IOException e) {
        		e.printStackTrace();
        	}
        	try {
        		fOut.close();
        	} catch (IOException e) {
        		e.printStackTrace();
        	}
        }
    }
    

    2. 在Android入口进行初始化

    打开AppActivity.java文件,复写OnCreate()与onActivityResult()方法。

    在OnCreate方法中对我们的类初始化:“ImagePicker.getInstance().init(this);”

    在onActivityResult()中将回调參数传递到ImagePicker中:“ImagePicker.getInstance().onActivityResult(requestCode, resultCode, data);”

    AppActivity.java详细代码:

    public class AppActivity extends Cocos2dxActivity {
    
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		// TODO Auto-generated method stub
    		super.onCreate(savedInstanceState);
    		
    		ImagePicker.getInstance().init(this);
    	}
    
    	@Override
    	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    		// TODO Auto-generated method stub
    		super.onActivityResult(requestCode, resultCode, data);
    		
    		ImagePicker.getInstance().onActivityResult(requestCode, resultCode, data);
    	}
    }

    3. 最后在AndroidManifest.xml增加訪问相机权限"<uses-permission android:name="android.permission.CAMERA"/>"


    ------------------------------------------

                             IOS

    ------------------------------------------

    1. 将開始创建的ImagePicker.cpp文件后缀名改为.mm文件。

    2. 在proj.ioc_mac/ios目录下创建两个文件。分别为ImagePickerViewController.h、ImagePickerViewController.mm

    ImagePickerViewController.h详细代码:

    #import <UIKit/UIKit.h>
    
    @interface ImagePickerViewController : UIViewController<UINavigationControllerDelegate, UIImagePickerControllerDelegate>
    {
        NSString* filePath;
    }
    
    // 打开本地相冊
    - (void)localPhoto;
    
    // 打开相机
    - (void)takePhoto;
    
    
    @end

    ImagePickerViewController.mm详细代码
    #import "ImagePickerViewController.h"
    #import "cocos2d.h"
    
    @interface ImagePickerViewController ()
    
    @end
    
    @implementation ImagePickerViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        //[self localPhoto];
    }
    
    - (void)viewDidUnload
    {
        [super viewDidUnload];
    }
    
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    
    -(void)localPhoto{
        UIImagePickerController *picker = [[UIImagePickerController alloc] init];
        picker.delegate      = self;
        picker.sourceType    = UIImagePickerControllerSourceTypePhotoLibrary;
        picker.allowsEditing = YES;
        //[self presentModalViewController:picker animated:YES];
        [self presentViewController:picker animated:YES completion:^(void){
            NSLog(@"Imageviewcontroller is presented");
        }];
        [picker release];
        
        NSLog(@"-(void)localPhoto();");
    }
    
    - (void)takePhoto{
        UIImagePickerControllerSourceType sourceType = UIImagePickerControllerSourceTypeCamera;
        if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
            UIImagePickerController* picker = [[UIImagePickerController alloc] init];
            picker.delegate = self;
            //设置拍照后的图像可编辑
            picker.allowsEditing = YES;
            picker.sourceType = sourceType;
            [picker release];
            [self presentModalViewController:picker animated:YES];
        }
        else{
            NSLog(@"模拟器中无法打开照相机,请在真机中调试");
        }
    }
    
    - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
        NSString *type = [info objectForKey:UIImagePickerControllerMediaType];
        
        //当选择的类型是图片
        if ([type isEqualToString:@"public.image"])
        {
            //先把图片转成NSData
    //        UIImage* image = [info objectForKey:@"UIImagePickerControllerOriginalImage"];
            UIImage* image = [info objectForKey:@"UIImagePickerControllerEditedImage"];
            NSData *data;
            if (UIImagePNGRepresentation(image) == nil)
            {
                data = UIImageJPEGRepresentation(image, 1.0);
            }
            else
            {
                data = UIImagePNGRepresentation(image);
            }
            
            //图片保存的路径
            //这里将图片放在沙盒的documents目录中
            NSString * DocumentsPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"];
            
            //文件管理器
            NSFileManager *fileManager = [NSFileManager defaultManager];
            
            //生成唯一字符串
            NSString* uuid = [[NSUUID UUID] UUIDString];
            
            //文件名称
            NSString* fileName = [NSString stringWithFormat:@"/%@.png", uuid];
            
            //把刚刚图片转换的data对象拷贝至沙盒中 并保存为XXXXXXXX-XXXX-XXXX....XXXX.png
            [fileManager createDirectoryAtPath:DocumentsPath withIntermediateDirectories:YES attributes:nil error:nil];
            [fileManager createFileAtPath:[DocumentsPath stringByAppendingString:fileName] contents:data attributes:nil];
            
            
            //得到选择后沙盒中图片的完整路径
            filePath = [[NSString alloc]initWithFormat:@"%@%@", DocumentsPath, fileName];
            
            //关闭相冊界面
            [picker dismissModalViewControllerAnimated:YES];
            
            std::string strFilePath = [filePath UTF8String];
            cocos2d::Director::getInstance()->getEventDispatcher()->dispatchCustomEvent("ImagePickerEvent", &strFilePath);
        }
        
    }
    
    - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker{
        NSLog(@"您取消了选择图片");
        [picker dismissModalViewControllerAnimated:YES];
    }
    
    @end
    

    3. 改动原projectAppController.mm文件,增加一行“ImagePicker::getInstance()->setViewController(_viewController);”用来初始化。

    AppController.mm详细代码:

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    
    
        cocos2d::Application *app = cocos2d::Application::getInstance();
        app->initGLContextAttrs();
        cocos2d::GLViewImpl::convertAttrs();
    
        // Override point for customization after application launch.
    
        // Add the view controller's view to the window and display.
        window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]];
    
        // Init the CCEAGLView
        CCEAGLView *eaglView = [CCEAGLView viewWithFrame: [window bounds]
                                             pixelFormat: (NSString*)cocos2d::GLViewImpl::_pixelFormat
                                             depthFormat: cocos2d::GLViewImpl::_depthFormat
                                      preserveBackbuffer: NO
                                              sharegroup: nil
                                           multiSampling: NO
                                         numberOfSamples: 0 ];
    
        // Use RootViewController manage CCEAGLView 
        _viewController = [[RootViewController alloc] initWithNibName:nil bundle:nil];
        _viewController.wantsFullScreenLayout = YES;
        _viewController.view = eaglView;
        
        //----------------------------------
        // 初始化ImagePicker
        //----------------------------------
        ImagePicker::getInstance()->setViewController(_viewController);
        //----------------------------------
        
        // Set RootViewController to window
        if ( [[UIDevice currentDevice].systemVersion floatValue] < 6.0)
        {
            // warning: addSubView doesn't work on iOS6
            [window addSubview: _viewController.view];
        }
        else
        {
            // use this method on ios6
            [window setRootViewController:_viewController];
        }
    
        [window makeKeyAndVisible];
        
    
        [[UIApplication sharedApplication] setStatusBarHidden:true];
    
        // IMPORTANT: Setting the GLView should be done after creating the RootViewController
        cocos2d::GLView *glview = cocos2d::GLViewImpl::createWithEAGLView(eaglView);
        cocos2d::Director::getInstance()->setOpenGLView(glview);
    
        app->run();
        
        return YES;
    }
    



    OK。接下来就是在C++代码中调用了,你仅仅须要在想使用头像选择器的时候,调用这一行代码:
    ImagePicker::getInstance()->callImagePickerWithPhotoAndCamera([=](std::string path)
        {
            //做你想做的事情
        });
    忘了说明,參数path就是你已经选择并编辑后。图片的真实路径。



    咳咳,请各位大神轻喷,喜欢游戏的程序孩纸伤不起~别说,回家LOL过去的~


    版权声明:本文博主原创文章,博客,未经同意不得转载。

  • 相关阅读:
    PHP $_SERVER['HTTP_REFERER'] 获取前一页面的 URL 地址
    LAMP与LNMP架构的区别及其具体的选择说明
    LNMP 与 LAMP 架构的区别及配置解决方案
    LAMP和LNMP,你更愿意选择谁,为什么?
    Storm流计算从入门到精通之技术篇(高并发策略、批处理事务、Trident精解、运维监控、企业场景)
    Zookeeper从入门到精通(开发详解,案例实战,Web界面监控)
    基于Greenplum Hadoop分布式平台的大数据解决方案及商业应用案例剖析
    深入浅出Hive企业级架构优化、Hive Sql优化、压缩和分布式缓存(企业Hadoop应用核心产品)
    深入浅出OpenStack云计算平台管理(nova-compute/network)
    玩转大数据:深入浅出大数据挖掘技术(Apriori算法、Tanagra工具、决策树)
  • 原文地址:https://www.cnblogs.com/hrhguanli/p/4877186.html
Copyright © 2020-2023  润新知