推广

SpringBoot2.x整合(cos,oss,本地挂载盘)文件存储的小而美的项目实现

iseeyu3年前 (2024-02-22)推广161

image.png

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.tellme.os.config.OSAutoConfiguration

2.3 client端(适配器模式)

首先是需要提供一个项目内公共的接口类(OSClient)。无论各个厂商的client客户端都要进行适配。

public interface OSClient {

    /**
     * 上传文件
     *
     * @param storageName  (bucketName)存储桶
     * @param relativePath 文件上传的地址
     * @param fileBytes    文件流
     */
    void uploadFile(String storageName, String relativePath, byte[] fileBytes);

    /**
     * 下载文件
     *
     * @param filePath 文件地址
     * @return 文件输出流
     */
    ByteArrayOutputStream downloadFile(String filePath);

    /**
     * 客户端关闭
     */
    void shutdown();
}

由于云文件存储,下载文件的方法是相同的,故可以使用模板方法模式来进行扩展。

@Slf4j
public abstract class NetOsClient implements OSClient {

    @Override
    public ByteArrayOutputStream downloadFile(String filePath) {
        ByteArrayOutputStream data;
        data = OkHttpUtil.get(filePath);
        if (data == null) {
            throw new OsException("下载照片失败,照片地址:" + filePath);
        }
        //转换流信息
        return data;
    }

}

2.3.1 适配腾讯云cos客户端

@Data
public class CosClient extends NetOsClient {

    private COSClient cosClient;

    /**
     *
     * @param bucketName   存储桶
     * @param relativePath 文件上传的地址
     * @param fileBytes    文件流
     */
    @Override
    public void uploadFile(String bucketName, String relativePath, byte[] fileBytes) {
        ByteArrayInputStream input = new ByteArrayInputStream(fileBytes);
        ObjectMetadata objectMetadata = new ObjectMetadata();
        // 设置输入流长度
        objectMetadata.setContentLength(input.available());
        cosClient.putObject(bucketName, relativePath, input, objectMetadata);
    }
    /**
     * 关闭客户端
     */
    @Override
    public void shutdown() {
        cosClient.shutdown();
    }
}

2.3.2 适配阿里云oss客户端

@Data
public class OssClient extends NetOsClient{

    private OSSClient ossClient;

    @Override
    public void uploadFile(String bucketName, String relativePath, byte[] fileBytes) {
        ByteArrayInputStream input = new ByteArrayInputStream(fileBytes);
        ObjectMetadata objectMetadata = new ObjectMetadata();
        // 设置输入流长度
        objectMetadata.setContentLength(input.available());
        ossClient.putObject(bucketName, relativePath, input, objectMetadata);
    }

    @Override
    public void shutdown() {
        ossClient.shutdown();
    }
}

2.3.3 适配本地挂载盘客户端

@Slf4j
public class FileOsClient implements OSClient {

    /**
     * @param bucketName  上传的根路径
     * @param relativePath 文件上传的地址
     * @param fileBytes    文件流
     */
    @Override
    public void uploadFile(String bucketName, String relativePath, byte[] fileBytes) {
        File file = new File(bucketName + File.separator + relativePath);
        //获取文件夹目录
        String fullPath = file.getParent();
        File dir = new File(fullPath);
        //创建目录
        dir.mkdirs();
        try {
            //构建输出流
            ByteArrayInputStream is = new ByteArrayInputStream(fileBytes);
            FileOutputStream os = new FileOutputStream(file);
            byte[] b = new byte[1024];
            int nRead;
            while ((nRead = is.read(b)) != -1) {
                os.write(b, 0, nRead);
            }
        } catch (Exception e) {
            log.error("", e);
            throw new OsException("", e);
        }
    }

    /**
     * 读取本地文件
     *
     * @param filePath 文件地址
     * @return 文件数组输出流
     */
    @Override
    public ByteArrayOutputStream downloadFile(String filePath) {
        File file = new File(filePath);
        if (!file.exists()) {
            throw new OsException("文件不存在!");
        }
        InputStream is = IOUtil.getInputStream(file);
        return IOUtil.convertInputStream(is);
    }

    @Override
    public void shutdown() {

    }

}

2.4 template类(模板模式+策略模式)

  1. template通过组合的方式引入client对象,在Spring容器初始化的时候选择合适的client策略。
  2. template采用模板方法模式,可自由扩展。
public interface OsTemplate {

    /**
     * 上传文件
     *
     * @param relativePath 上传文件的相对地址
     * @param fileBytes    文件流
     * @return 上传后的地址
     */
    String saveFile(String relativePath, byte[] fileBytes);

    /**
     * 拼接路径,上传照片
     *
     * @param modulePath 项目路径
     * @param fileSuffix 文件后缀
     * @param base64Str  base64
     * @return 格式:{@code PATH_COMMON / modulePath / calculatePath() /UUID.randomUUID() + fileSuffixEnum.getType()}
     */
    String saveFile(String modulePath, String fileSuffix, String base64Str);

    /**
     * 拼接路径,上传文件
     *
     * @param modulePath 项目路径
     * @param fileBytes  文件流
     * @param fileSuffix 文件后缀
     * @return 格式:{@code PATH_COMMON / modulePath / calculatePath() /UUID.randomUUID() + fileSuffixEnum.getType()}
     */
    String saveFile(String modulePath, byte[] fileBytes, String fileSuffix);


    /**
     * 拼接路径,上传到特定的存储桶
     *
     * @param bucketName 存储桶的名字
     * @param modulePath 项目路径
     * @param fileSuffix 文件后缀
     * @param fileBytes  文件流
     * @return 格式:{@code PATH_COMMON / modulePath / calculatePath() /UUID.randomUUID() + fileSuffix}
     */
    String saveFile(String bucketName, String modulePath, String fileSuffix, byte[] fileBytes);

    /**
     * 拼接路径,上传到特定的存储桶
     *
     * @param bucketName   存储桶的名字
     * @param relativePath 文件的相对路径
     * @param fileBytes    文件流
     * @return 格式:{@code PATH_COMMON / modulePath / calculatePath() /UUID.randomUUID() + fileSuffix}
     */
    String saveFile(String bucketName, String relativePath, byte[] fileBytes);


    /**
     * 文件下载
     *
     * @param filePath 文件地址
     * @return 下载字节流
     */
    ByteArrayOutputStream downloadFile(String filePath);

    /**
     * CDN前缀地址
     *
     * @return CDN地址
     */
    String getCdnHost();
}

2.4.1 云存储的template

@Data
@Slf4j
public class NetOsTemplate implements OsTemplate {

    //cos连接对象
    protected OSClient client;

    protected OsConfigProperties osConfigProperties;

    /**
     * 存储介质:
     * cos存储的是:bucketName
     * file存储的是:根路径
     */
    protected String bucketName;

    /**
     * cdn地址
     */
    protected String cdnHost;

    protected static  String PATH_COMMON = "/xos";


    /**
     * (经常使用)使用配置文件的存储桶上传照片
     *
     * @param modulePath 模块路径
     * @param fileSuffix 文件后缀,需要携带.
     * @param base64Str  照片的base64信息
     * @return 图片上传的地址
     */
    public String saveFile(String modulePath, String fileSuffix, String base64Str) {
        return saveFile(bucketName, modulePath, fileSuffix, IOUtil.base64ToBytes(base64Str));
    }


    /**
     * (经常使用)使用配置文件的存储桶上传文件
     *
     * @param modulePath 模块路径
     * @param fileSuffix 文件后缀,需要携带.
     * @param fileBytes  文件流字节数组
     * @return 图片上传的地址
     */
    public String saveFile(String modulePath, byte[] fileBytes, String fileSuffix) {
        return saveFile(bucketName, modulePath, fileSuffix, fileBytes);
    }

    /**
     * 腾讯云上传文件
     * 只需要传入模块目录,自动拼装上传腾讯云的路径。
     * {@code PATH_COMMON / modulePath / calculatePath() /UUID.randomUUID() + fileSuffixEnum.getType()}
     *
     * @param bucketName 存储目录
     * @param modulePath  模块路径(业务路径格式:{@code exercise/plan/answerImage })
     * @param fileSuffix  文件后缀,需要携带.
     * @param fileBytes   文件流字节数组
     * @return 图片上传的地址
     */
    public String saveFile(String bucketName, String modulePath, String fileSuffix, byte[] fileBytes) {
        return saveFile(bucketName, getRelativeDir(modulePath) + getFileName(fileSuffix), fileBytes);
    }

    /**
     * 腾讯云上传文件
     * 返回的路径:{@code PATH_COMMON / modulePath / calculatePath() /UUID.randomUUID() + fileSuffix}
     *
     * @param relativePath 上传文件的相对地址
     * @param fileBytes    文件流
     * @return 保存到腾讯云的地址
     */
    @Override
    public String saveFile(String relativePath, byte[] fileBytes) {
        //读取配置信息的存储桶信息
        return saveFile(bucketName, relativePath, fileBytes);
    }

    /**
     * 腾讯云上传文件
     *
     * @param bucketName  存储桶
     * @param relativePath 文件的相对地址
     * @param fileBytes    文件流的字节数组
     * @return 腾讯云上的相对地址
     */
    public String saveFile(String bucketName, String relativePath, byte[] fileBytes) {
        client.uploadFile(bucketName, relativePath, fileBytes);
        return relativePath;
    }


    /**
     * 文件下载
     * 若下载失败或者照片不存在,抛出{@link com.xdf.pscommon.os.exception.OsException}异常。
     *
     * @param filePath 文件地址
     * @return 文件输出流
     */
    @Override
    public ByteArrayOutputStream downloadFile(String filePath) {
        return client.downloadFile(filePath);
    }


    /**
     * 获取腾讯云的前缀地址
     *
     * @return 腾讯云的CDN地址
     */
    public String getCdnHost() {
        return cdnHost;
    }

    /**
     * 创建文件夹的相对路径
     *
     * @return 格式:{@code exam/modulePath/2021/11/1}
     */
    protected String getRelativeDir(String modulePath) {
        return PATH_COMMON + File.separator + modulePath + File.separator + calculatePath();
    }

    /**
     * 创建文件名(使用UUID创建)
     */
    protected String getFileName(String fileSuffix) {
        return UUID.randomUUID() + fileSuffix;
    }

    /**
     * 时间戳作为路径名
     */
    protected String calculatePath() {
        Calendar calendar = Calendar.getInstance();
        String year = String.valueOf(calendar.get(Calendar.YEAR));
        String month = String.valueOf(calendar.get(Calendar.MONTH) + 1);
        String date = String.valueOf(calendar.get(Calendar.DATE));
        return year + "/" + month + "/" + date + "/";
    }

}

2.4.2 挂载盘存储的template

挂载盘在创建目录时,需要随机路由到不同的挂载盘上,所以需要重写getRelativeDir方法即可。

public class FileOsTemplate extends NetOsTemplate {

    private long index;

    private final static String ROOTPATH = "rootPath";
    private final static String FORWARDMARK = "forwardMark";


    /**
     * 路径填充挂载盘路径
     */
    @Override
    public String getRelativeDir(String modulePath) {
        OperatingSystemEnum operatingSystem = IOUtil.getOperatingSystem();
        if (OperatingSystemEnum.LINUX.equals(operatingSystem) &&
                osConfigProperties.getFile().getRootPath() != null &&
                osConfigProperties.getFile().getRootPath().size() > 1) {
            Map<String, String> rootPathMap = returnRootPathMap(osConfigProperties.getFile().getRootPath());
            return PATH_COMMON + File.separator + rootPathMap.get(FORWARDMARK) + File.separator + modulePath + File.separator + calculatePath();
        } else {
            return super.getRelativeDir(modulePath);
        }
    }

    /**
     * 返回挂载盘的具体配置
     */
    private Map<String, String> returnRootPathMap(Map<String, String> rootPaths) {
        int size = 2; //最少有个原磁盘以及加的共享盘,共享磁盘个数
        if (rootPaths != null && rootPaths.size() > 1) {
            size = rootPaths.size() - 1;
        }
        int rootPathIndex = Math.toIntExact(index++ % size);
        Map<String, String> rootPathMap = new HashMap<>();
        switch (rootPathIndex) {
            case 0:
                rootPathMap.put(ROOTPATH, rootPaths.get("files1"));
                rootPathMap.put(FORWARDMARK, "files1/");
                break;
            case 1:
                rootPathMap.put(ROOTPATH, rootPaths.get("files2"));
                rootPathMap.put(FORWARDMARK, "files2/");
                break;
            case 2:
                rootPathMap.put(ROOTPATH, rootPaths.get("files3"));
                rootPathMap.put(FORWARDMARK, "files3/");
                break;
            default:
                rootPathMap.put(ROOTPATH, rootPaths.get("files"));
                rootPathMap.put(FORWARDMARK, "files/");
                break;
        }
        return rootPathMap;
    }

}

2.5 加入到Spring容器中

  1. 使用@ConditionalOnClass注解,当某个依赖存在时,才加载bean类;
  2. 使用@ConditionalOnProperty注解,当某个配置存在时,才加载bean类;
  3. 使用@Import注解,当多个配置均存在时,优先加载哪个配置;
  4. 配合@ConditionalOnMissingBean(name = "netOsTemplate")注解,当加载某个配置后,便不加载同名的bean。

2.5.1 配置类

当本地磁盘加载时,支持windows,mac,linux各个配置。

@Data
@ConfigurationProperties("xos")
public class OsConfigProperties {

    /**
     * 腾讯云COS配置
     */
    private CosProperties cos;

    /**
     * 阿里云OOS配置
     */
    private OssProperties oss;
    /**
     * 共享盘文件存储配置
     */
    private FileProperties file;

    /**
     * 云存储——存储桶
     */
    private String bucketName;
    /**
     * 云存储——cdn地址
     */
    private String cdnHost;

    @Data
    public static class CosProperties {

        private String accessKeyId;

        private String accessKeySecret;

        private String region;


    }


    @Data
    public static class OssProperties {
        private String endpoint;

        private String accessKeyId;

        private String accessKeySecret;

    }


    @Data
    public static class FileProperties {
        /**
         * Linux支持的根路径
         */
        private String linuxRootPath;
        /**
         * window支持的根路径
         */
        private String windowsRootPath;
        /**
         * mac支持的根路径
         */
        private String macRootPath;

        /**
         * Linux系统设置,挂载盘映射关系
         */
        private Map<String, String> rootPath;
        /**
         * cdn地址
         */
        private String cdnHost;
    }
}

2.5.2 腾讯云cos的client加入到Spring中

只有存在COSClient.class类且存在xos.cos.accessKeyId配置时,才会加载改bean。

@Configuration
@ConditionalOnClass(COSClient.class)
public class CosClientConfiguration {

    /**
     * 初始化腾讯云客户端
     */
    @Bean(name = "netOSClient", destroyMethod = "shutdown")
    @ConditionalOnProperty("xos.cos.accessKeyId")
    @ConditionalOnMissingBean(name = "netOSClient")
    public OSClient cosClient(OsConfigProperties osConfigProperties) {
        CosClient cosClient = new CosClient();
        OsConfigProperties.CosProperties cosProperties = osConfigProperties.getCos();
        // 1 初始化用户身份信息(secretId, secretKey)。
        COSCredentials cred = new BasicCOSCredentials(cosProperties.getAccessKeyId(), cosProperties.getAccessKeySecret());
        // 2 设置 bucket 的区域, COS 地域的简称请参照 https://cloud.tencent.com/document/product/436/6224
        // clientConfig 中包含了设置 region, https(默认 http), 超时, 代理等 set 方法, 使用可参见源码或者常见问题 Java SDK 部分。
        Region cosRegion = new Region(cosProperties.getRegion());
        ClientConfig clientConfig = new ClientConfig(cosRegion);
        // 3 生成 cos 客户端。
        COSClient cosClient = new COSClient(cred, clientConfig);
        cosClient.setCosClient(cosClient);
        return cosClient;
    }

}

2.5.3 阿里云oss的client加入到Spring容器

@Configuration
@ConditionalOnClass(OSSClient.class)
public class OssClientConfiguration {

    /**
     * 初始化阿里云oss客户端
     */
    @Bean(name = "netOSClient", destroyMethod = "shutdown")
    @ConditionalOnProperty("xos.oss.accessKeyId")
    @ConditionalOnMissingBean(name = "netOSClient")
    public  OSClient ossClient(OsConfigProperties osConfigProperties) {
        OssClient ossClient = new OssClient();
        OsConfigProperties.OssProperties ossProperties = osConfigProperties.getOss();
        OSSClient ossClient = new OSSClient(ossProperties.getEndpoint(), ossProperties.getAccessKeyId(), ossProperties.getAccessKeySecret());
        ossClient.setOssClient(ossClient);
        return ossClient;
    }
}

2.5.4 自动装配的config

  1. @EnableConfigurationProperties将配置文件类加入到Spring容器;
  2. @Import({CosClientConfiguration.class,OssClientConfiguration.class})将config类加入到Spring容器;
@Slf4j
@Configuration
@EnableConfigurationProperties(value = OsConfigProperties.class)
@Import({CosClientConfiguration.class,OssClientConfiguration.class})
public class OSAutoConfiguration {



    /**
     * 初始化文件流客户端
     */
    @Bean
    @ConditionalOnMissingBean(name = "fileClient")
    public lFileOsClient fileClient(OsConfigProperties osConfigProperties) {
        return new FileOsClient();
    }


    /**
     * 创建云存储的模板类
     *
     * @param oSClient           cos的客户端
     * @param osConfigProperties 配置信息
     * @return 模板工具类
     */
    @Bean
    @Primary
    @ConditionalOnBean(name = {"netOSClient"})
    @ConditionalOnMissingBean(name = "netOsTemplate")
    public NetOsTemplate netOsTemplate(@Qualifier("netOSClient") OSClient oSClient, OsConfigProperties osConfigProperties) {
        NetOsTemplate osTemplate = new NetOsTemplate();
        //放入客户端
        osTemplate.setClient(oSClient);
        osTemplate.setBucketName(osConfigProperties.getBucketName());
        //配置文件
        osTemplate.setOsConfigProperties(osConfigProperties);
        osTemplate.setCdnHost(osConfigProperties.getCdnHost());
        return osTemplate;
    }

    /**
     * 共享盘OS的模板类
     */
    @Bean
    @ConditionalOnMissingBean(name = "fileOsTemplate")
    public OsTemplate fileOsTemplate(FileOsClient cosClient, OsConfigProperties osConfigProperties) {
        FileOsTemplate osTemplate = new FileOsTemplate();
        //放入客户端
        osTemplate.setClient(cosClient);
        OperatingSystemEnum operatingSystem = IOUtil.getOperatingSystem();
        osConfigProperties.FileProperties fileProperties =osConfigProperties.getFile();
        //获取对应的根路径
        String bucketName = null;
        String cdnHost = null;
        if (fileProperties != null) {
            if (OperatingSystemEnum.WINDOWS.equals(operatingSystem)) {
                bucketName = fileProperties.getWindowsRootPath();
            } else if (OperatingSystemEnum.MAC.equals(operatingSystem)) {
                bucketName = fileProperties.getMacRootPath();
            } else {
                bucketName = fileProperties.getLinuxRootPath();
            }
            cdnHost = fileProperties.getCdnHost();
        }
        if (bucketName == null) {
            log.warn("File root path is null. use '/' is root");
            //默认是根路径
            bucketName = "/";
        }
        if (cdnHost == null) {
            log.warn("File cdnHost is null.");
        }
        osTemplate.setCdnHost(cdnHost);

        //设置存储名字
        osTemplate.setBucketName(bucketName);
        //配置文件
        osTemplate.setOsConfigProperties(osConfigProperties);
        return osTemplate;
    }
}

2.6 工具类

@Slf4j
public abstract class IOUtil {


    /**
     * 输入流转换为输出流
     *
     * @param is 输入流
     * @return 输出流
     */
    public static ByteArrayOutputStream convertInputStream(InputStream is) {
        final byte[] by = new byte[1024];
        ByteArrayOutputStream data = new ByteArrayOutputStream();
        try {
            // 将内容读取内存中
            int len;
            while ((len = is.read(by)) != -1) {
                data.write(by, 0, len);
            }
        } catch (IOException e) {
            log.error("convertInputStream IO Exception", e);
        }
        return data;
    }

    /**
     * base64转化为byte[]字节流
     *
     * @param base64Str 照片的base64
     * @return 字节流
     */
    public static byte[] base64ToBytes(String base64Str) {
        if (StringUtils.isBlank(base64Str)) {
            throw new SealOsException("base64Str is blank!");
        }
        return Base64.decodeBase64(base64Str);
    }

    /**
     * 获取文件输入流
     *
     * @param file 文件对象
     * @return 文件输入流对象
     */
    public static InputStream getInputStream(File file) {
        FileInputStream fin;
        try {
            fin = new FileInputStream(file);
        } catch (FileNotFoundException e) {
            if (log.isDebugEnabled()) {
                log.debug(String.valueOf(e));
            }
            String msg = "找不到指定的文件[" + file.getName() + "]。";
            if (log.isDebugEnabled()) {
                log.debug(msg);
            }
            throw new SealOsException(msg, e);
        }
        return fin;
    }

    /**
     * 获取当前系统的操作系统类型
     */
    public static OperatingSystemEnum getOperatingSystem() {
        if (System.getProperty("os.name").toUpperCase().contains("WINDOWS")) {
            return OperatingSystemEnum.WINDOWS;
        } else if (System.getProperty("os.name").toUpperCase().contains("MAC")) {
            return OperatingSystemEnum.MAC;
        } else {
            return OperatingSystemEnum.LINUX;
        }
    }

    /**
     * 文件后缀
     *
     * @param fileName 文件名或者文件路径
     * @return 后缀类型
     */
    public static String getFileSuffix(String fileName) {
        if (fileName == null) {
            return null;
        }
        return fileName.substring(fileName.lastIndexOf("."));
    }

    /**
     * 获取文件名
     */
    public static String getFileName(String filePath) {
        if (filePath == null) {
            return null;
        }
        File file=new File(filePath);
        return file.getName();
    }

    /**
     * 获取目录
     */
    public static String getDirName(String filePath) {
        if (filePath == null) {
            return null;
        }
        File file=new File(filePath);
        return file.getParent();
    }


}
public abstract class OkHttpUtil {

    /**
     * 最大连接时间
     */
    public final static int CONNECTION_TIMEOUT = 15;

    public final static Logger logger = LoggerFactory.getLogger(OkHttpUtil.class);

    /**
     * client
     * 配置重试
     */
    private final static OkHttpClient HTTP_CLIENT = new OkHttpClient.Builder()
            .connectTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS)
            .build();

    /**
     * get请求,无需转换对象
     *
     * @param url 链接
     * @return 响应信息
     */
    public static ByteArrayOutputStream get(String url) {
        try {
            Request request = new Request.Builder().url(url).build();
            Response response = HTTP_CLIENT.newCall(request).execute();

            if (response.isSuccessful() && response.body() != null) {
                InputStream inputStream = response.body().byteStream();
                return IOUtil.convertInputStream(inputStream);
            }
        } catch (Exception e) {
            logger.error("执行get请求,url: {} 失败!", url, e);
            throw new SealOsException(e);
        }
        return null;
    }
}

2.7 异常类

public class OsException extends RuntimeException {


    public OsException() {
    }

    public OsException(String message) {
        super(message);
    }

    public OsException(String message, Throwable cause) {
        super(message, cause);
    }

    public OsException(Throwable cause) {
        super(cause);
    }

    public OsException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}

扫描二维码推送至手机访问。

版权声明:本文由西安泽虎代运营发布,如需转载请注明出处。

转载请注明出处https://www.0291.com.cn/post/56128.html

相关文章

西安企业做百度推广,一个月花三千和花三万到底差在哪?

4|最近跟几个西安的老板聊天,发现一个挺有意思的现象:同样做百度推广,有人一个月花三千块,询盘不断;有人一个月砸三万,水花都没见着。问他们区别在哪,大多数人说不清楚,只知道'钱花了,没效果'。 5| 6|百度推广这东西,说复杂也复杂,说简单也简单。核心就一件事:让你的潜在客户搜某个词的时候,你的信息...

我想当策划,大学应该选什么专业?

我想当策划,大学应该选什么专业?

学,广告,传播,公关,这些专业其实都可以。另外也可以学像汉语言文学,以及一些跟文学相关的一些专业,会对你找工作有帮助。但是,你想从事这个这份职业,除了专业的学习之外,更多的是,你要在上学的时候积极参与一些更营销策划相关的社会实践。比如说你可以做一些兼职,比如说帮别人去写稿,...

服装市场营销策略分析,“定位不慎满盘皆输”

服装市场营销策略分析,“定位不慎满盘皆输”

不同的区域需要不同的策略,而不同的营销策略需要不同的销售思路来配合。因此,在研究女装产品销售思路之前,最好先对于市场与营销策略做一次深入地了解。1.市场区域化(market segmentation):目标营销的第一步,是将整个市场拆分成若干个同性质的小市场。2.目标市场的...

晚间悦读丨你的思维方式,决定你的人生高度!

晚间悦读丨你的思维方式,决定你的人生高度!

越长大,越发现,能够决定你人生高度的,往往是你的认知层次。 你的认知在哪个层级,你的人生就处在什么状态。你认知的高度,决定了你人生的高度。 那如何才能升级方式、提升认知能力呢?可以从这三个方面做出改变。 NO. 1 改变态度 有人...

编写项目营销策划方案六大要点

编写项目营销策划方案六大要点

现在商业发展非常迅速,的需求非常的大,但是要做好营销策划是有一定难度的,我把自己做营销策划的一些体会在这里分享给朋友们。营销策划方案六大要点我们先说项目营销策划方案这个话题。大部分的营销策划人都是在企业里工作,大部分项目之初都是来源于一个想法,创始人、老板或者创始团队的一个...

【商场营销策划】如何进行商场营销策划?

【商场营销策划】如何进行商场营销策划?

商场是策划者们关注的永恒话题,那么究竟该如何作好商场营销策划呢?掌握以下这几点营销策略设计原则,至关重要!1.义利兼顾的原则。商场在获取利润的同时要充分考虑到消费者的合法利益和商场的长远利益。在作商场活动营销策略方案的时候应该将这一点作为首要的原则。对商场有利而对顾客不利的...

现在,非常期待与您的又一次邂逅

我们努力让每一部企业宣传片和抖音短视频成为商业大片