常见的图片存储方案:

1.使用nginx搭建图片服务器

2.使用开源的分布式存储系统,例如Fastdfs,HDS等

3.使用云存储,例如阿里云,七牛云等

在实际的开发中,我们会有很多处理不同功能的服务器,例如:

应用服务器:负责部署我们的应用

数据库服务器:运行我们的数据库

文件服务器:负责存储用户上传文件的服务器

一下以使用云服务器为例:

spring配置:
<!--文件上传组件-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="maxUploadSize" value="104857600"/>
    <property name="maxInMemorySize" value="4096"/>
    <property name="defaultEncoding" value="UTF-8"/>
</bean>
上传文件工具类:
public class QiniuUtils {

    public static String accessKey = "xxxxxxxxxx";
    public static String secretKey = "xxxxxxxxxx";
    public static String bucket = "存储地址名";

    public static void uploadQiniu(String filePath,String fileName){
        //构造一个带指定Zone对象的配置类
        Configuration cfg = new Configuration(Zone.zone0());
        UploadManager uploadManager = new UploadManager(cfg);
        Auth auth = Auth.create(accessKey,secretKey);
        String upToken = auth.uploadToken(bucket);

        try{
            Response response = uploadManager.put(filePath,fileName,upToken);
            //解析上传成功的结果
            new Gson().fromJson(response.bodyString(),DefaultPutRet.class);
        }catch (QiniuException ex){
            Response r = ex.response;
            try{
                System.err.println(r.bodyString());
            }catch (QiniuException ex2){
                //ignore
            }
        }
    }

    //上传文件
    public static void upload2Qiniu(byte[] bytes, String fileName){
        Configuration cfg = new Configuration(Zone.zone0());
        //其他参数参考类注释
        UploadManager uploadManager = new UploadManager(cfg);

        //默认不指定key的情况下,以文件内容的hash值作为文件名
        String key = fileName;
        Auth auth = Auth.create(accessKey,secretKey);
        String upToken = auth.uploadToken(bucket);
        try{
            Response response = uploadManager.put(bytes,key,upToken);
            //解析上传成功的结果
            DefaulPutRet putRet = new Gson().fromJson(response.bodyString(),DefaultPutRet.class);
            System.out.println(putRet.key);
            System.out.println(putRet.hash);
        }catch (QiniuException ex){
            Response r =ex.response;
            System.err.println(r.toString());
            try{
                System.err.println(r.bodyString());
            }catch (QiniuException ex2){
                //ignore
            }
        }
    }

    //删除文件
    public static void deleteFileFromQiniu(String fileName){
        //构造一个带指定Zone对象的配置类
        Configuration cfg = new Configuration(Zone.zone0());
        String key = fileName;
        Auth auth = Auth.create(accessKey,secretKey);
        BucketManager bucketManager = new BucketManager(auth,cfg);
        try{
            bucketManager.delete(bucket,key);
        }catch (QiniuException ex){
            System.err.println(ex.code());
            System.err.println(ex.response.toString());
        }
    }
}
Controller类:
@RestController
@RequestMapping("/setmeal")
public class SetmealController {

    //文件上传
    //@RequestParam 获取文件参数,如果前端和Controller名称一致,就不用加
    @RequestMapping("/upload")
    public Result upload(@RequestParam("imgFile") MultipartFile imgFile){
        System.out.println(imgFile);  //测试
        String originalFilename = imgFile.getOriginalFilename();  //获取原始文件名 abc.jpg
        int index = originalFilename.lastIndexOf(".");  //获取最后一个点的位置
        String extention = originalFilename.substring(index - 1);  //获取扩展名 .jpg
        String fileName = UUID.randomUUID().toString()+extention;  //避免重复,产生随机名称
        try {
            //将文件上传到云上
            QiniuUtils.upload2Qiniu(imgFile.getBytes(),"fileName");  //文件名重复会覆盖
        }catch (IOException e){
            e.printStackTrace();
            return new Result(false, MessageConstant.UPLOAD_FAILD);
        }
        return new Result(false, MessageConstant.UPLOAD_SUCCESS,fileName);
    }
}

完善文件上传机制:

    前面只是完成了文件上传,但是用户只是上传了图片而没有最终保存相关信息到数据库,这时上传的图片就是垃圾图片,对于这些图片我们要定时清理来释放磁盘空间,这就需要我们能够区分出来那些事垃圾图片,那些不是。

    方案:

        利用redis来保存图片名称,:

        1.当用户上传图片后,将图片名称保存到redis的一个Set集合中,例如集合名称为setmealPicResources  //只是作预览显示用

        2.当用户添加套餐后,将图片名称保存到redis的另一个Set集合中,例如集合名称为setmealPicDbResources  //真正提交时保存

        3.计算setmealPicResources集合与setmealPicDbResources集合的差值,结果就是垃圾图片的名称集合,清理即可

spring redis配置:
<beans>
    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <property name="maxTotal">
            <value>200</value>
        </property>
        <property name="maxIdle">
            <value>50</value>
        </property>
        <property name="testOnBorrow" value="true"/>
        <property name="testOnReturn" value="true"/>
    </bean>
    <bean id="jedisPool" class="redis.clients.jedis.JedisPool">
        <constructor-arg name="poolConfig" ref="jedisPoolConfig"/>
        <constructor-arg name="host" value="127.0.0.1"/>
        <constructor-arg name="port" value="6379" type="int"/>
        <constructor-arg name="timeout" value="30000" type="int"/>
    </bean>

    <import resorces="spring-redis.xml"></import>
</beans>
定时任务配置
<beans>
    <!--开启spring注解使用-->
    <context:annotation-config></context:annotation-config>

    <!--注册自定义Job-->
    <bean id="ClearImgJob" class="com.xuyu.jobs.ClearImgJob"></bean>
    <!--注册JobDetail,作用是负责通过反射调用指定的Job-->
    <bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
        <!--注入目标对象-->
        <property name="targetObject" ref="clearImgJob"/>
        <!--注入目标方法-->
        <property name="targetObject" ref="clearImg"/>
    </bean>

    <!--注册一个触发器,指定任务触发的时间-->
    <bean id="myTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
        <!--注入JobDetail-->
        <property name="jobDetail" ref="jobDetail"/>
        <!--指定触发时间,基于Cron表达式-->
        <property name="cronExpression">
            <value>0 0 2 * * ?</value>
        </property>
    </bean>

    <!--注册一个统一的调度工厂,通过这个调度工厂调度任务-->
    <bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <!--注入多个触发器-->
        <property name="triggers">
            <list>
                <ref bean="myTrigger"/>
                <ref bean="myTrigger2"/>
            </list>
        </property>
    </bean>
</beans>
redis常量类:
public class RedisConstant {

    //套餐图片所有图片名称
    public static final String SETMEAL_PIC_RESOURCES = "setmealPicResources";
    //套餐图片保存在数据库中的图片名称
    public static final String SETMEAL_PIC_DB_RESOURCES = "setmealPicDbResources";
}
Controler更新:
@RestController
@RequestMapping("/setmeal")
public class SetmealController {

    @Autowired
    private JedisPool jedisPool;

    @Reference
    private SetmealService setmealService;

    //文件上传
    //@RequestParam 获取文件参数,如果前端和Controller名称一致,就不用加
    @RequestMapping("/upload")
    public Result upload(@RequestParam("imgFile") MultipartFile imgFile){
        System.out.println(imgFile);  //测试
        String originalFilename = imgFile.getOriginalFilename();  //获取原始文件名 abc.jpg
        int index = originalFilename.lastIndexOf(".");  //获取最后一个点的位置
        String extention = originalFilename.substring(index - 1);  //获取扩展名 .jpg
        String fileName = UUID.randomUUID().toString()+extention;  //避免重复,产生随机名称
        try {
            //将文件上传到云上
            QiniuUtils.upload2Qiniu(imgFile.getBytes(),"fileName");  //文件名重复会覆盖
            return new Result(false, MessageConstant.UPLOAD_SUCCESS,fileName);
            //将图片名称存入Redis,基于Redis的Set集合存储
            jedisPool.getResource().sadd(RedisConstant.SETMEAL_PIC_RESOURCES,fileName);
        }catch (IOException e){
            e.printStackTrace();
            return new Result(false, MessageConstant.UPLOAD_FAILD);
        }
    }
}
实现类:
@Service(interfaceClass = SetmealService.class)
@Transaction
public class SetmealServiceImpl implements SetmealService {

    @Autowired
    private SetmealDao setmealDao;

    @Autowired
    private JedisPool jedisPool;

    //新增套餐信息,同时需要关联检查组
    public void add(Setmeal setmeal, Integer[] checkgroupIds) {
        setmealDao.add(setmeal);
        Integer id = setmeal.getId();
        this.setSetmealAndCheckgroup(id,checkgroupIds);

        //将图片名称保存到Redis集合中
        String fileName = setmeal.getImg();
        jedisPool.getResource().sadd(RedisConstant.SETMEAL_PIC_DB_RESOURCES);
    }

    //设置套餐和检查组多对多关系,操作t_setmeal_checkgroup
    public void setSetmealAndCheckgroup(Integer setmealId,Integer[] checkgroupIds){
        if(checkgroupIds != null && checkgroupIds.length >0){
            for (Integer checkgroupId : checkgroupIds) {
                Map<String,Integer> map = new HashMap<String,Integer>();
                map.put("setmealId",setmealId);
                map.put("checkgroupId",checkgroupId);
                setmealDao.setmealAndCheckGroup(map);
            }
        }
    }
}
定时任务
/**
 * 自定义Job,实现定时清理垃圾图片
 */
public class ClearImgJob {

    @Autowired
    private JedisPool jedisPool;

    public void clearImg(){
        //根据Redis中保存的两个set集合进行差值计算,获得垃圾图片名称集合
        Set<String> set = jedisPool.getResource().sdiff(RedisConstant.SETMEAL_PIC_RESOURCES,RedisConstant.SETMEAL_PIC_DB_RESOURCES);
        //遍历set集合
        if(set != null){
            for (String picName : set) {
                //清理垃圾图片
                QiniuUtils.deleteFileFromQiniu(picName);
                //从Redis集合中删除图片名称
                jedisPool.getResource().srem(RedisConstant.SETMEAL_PIC_RESOURCES,picName);
            }
        }
    }
}

 

 

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐