Два простых примера создания файлового хранилища в СУБД

от автора

Практически в каждом веб-проекте требуется собственное хранилище файлов. Назначений у него множество. Сегодня мы рассмотрим 2 простых варианта его создания: первый — с использованием типа данных blob средствами Java, Spring MVC, Hibernate, MySQL и второй — с кластеризацией (разбиением файла на кусочки) средствами groovy, grails, hibernate, PostgreSQL.

Зачем нужен этот велосипед? Зачастую нужно отдавать пользователю сформированные на стороне сервера файлы и предусмотреть возможность самому выкладывать туда что-нибудь. К тому же, мы работаем с СУБД, к которой можно подключиться по JDBC с других хостов, и если сделать реплицируемую базу с несколькими нодами, то получится хорошая балансировка нагрузки на скачивание.

Вариант 1 — Использование типа данных blob

Итак, у нас есть Spring MVC. Создаём бин с persistence слоя, в котором заложен функционал “объекта”, хранимого в базе

DataObject.java

package ru.cpro.uchteno.domain.attach;  import java.sql.Blob;  import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Lob; import javax.persistence.Table;  import org.hibernate.annotations.Type;  @Entity @Table(name = "data_object") public class DataObject {          @Id @GeneratedValue(strategy=GenerationType.IDENTITY)     @Column(name="id")     private Integer id;          @Column(name="data_name")     private String name;          @Column(name="data_data", columnDefinition="longblob", length=2*1024*1024*1024)     @Lob()     private Blob data;          @Column(name="contype")     private String contentType;          @Column(name="surname")     private String surname;          @Column(name="access_count")     private Integer accessCount;      public Integer getId() {         return id;     }      public void setId(Integer id) {         this.id = id;     }      public String getName() {         return name;     }      public void setName(String name) {         this.name = name;     }      public Blob getData() {         return data;     }      public void setData(Blob data) {         this.data = data;     }      public String getContentType() {         return contentType;     }      public void setContentType(String contentType) {         this.contentType = contentType;     }      public String getSurname() {         return surname;     }      public void setSurname(String surname) {         this.surname = surname;     }      public Integer getAccessCount() {         return accessCount;     }      public void setAccessCount(Integer accessCount) {         this.accessCount = accessCount;     }      } 

private Integer id — идентификатор, первичный ключ нашего объекта;
private String name — имя загруженного объекта;
private Blob data — данные объекта;
private String contentType — тип данных объекта;
private String surname — видимое пользователем имя объекта;
private Integer accessCount — количество скачиваний;

Теперь опишем интерфейс дао слоя для нашего объекта.

AttachmentDAO.java

package ru.cpro.uchteno.dao.attach;  import java.util.List;  import ru.cpro.uchteno.domain.attach.DataObject;  public interface AttachmentDAO {     //Получить объект по айдишнику.     public DataObject getObjectByID(Integer id);     //Сохранить объект.     public void saveOrUpdate(DataObject dao);     //удалить объект.     public void deleteDataObject(DataObject dao);     //Получить список всех объектов.     public List<DataObject> listObjects();     //Получить объект по “фамилии”, видимому узеру имени.     public DataObject getObjectBySurname(String surname);     //Поиск объектов в базе по имени.     public List<DataObject> searchObjectsByName(String query); } 

Делаем реализацию интерфейса:

AttachmentDAOImpl.java

package ru.cpro.uchteno.dao.attach;  import java.util.ArrayList; import java.util.List;  import org.hibernate.SQLQuery; import org.hibernate.Session; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.orm.hibernate3.HibernateTemplate; import org.springframework.stereotype.Repository;  import ru.cpro.uchteno.domain.attach.DataObject;  @Repository public class AttachmentDAOImpl implements AttachmentDAO { 	//Внедряем hibernateTemplate 	@Autowired 	private HibernateTemplate attachHibernateTemplate;  	@Override 	public List<DataObject> listObjects() { 		return attachHibernateTemplate.find("from DataObject"); 	}  	@Override 	public DataObject getObjectByID(Integer id) { 		List<DataObject> doList = attachHibernateTemplate.find( 				"from DataObject where id = ?", id); 		if (doList == null || doList.size() <= 0) 			return null; 		return doList.get(0); 	}  	@Override 	public void saveOrUpdate(DataObject dao) { 		attachHibernateTemplate.saveOrUpdate(dao); 	}  	@Override 	public void deleteDataObject(DataObject dao) { 		attachHibernateTemplate.delete(dao); 	}  	@Override 	public DataObject getObjectBySurname(String surname) { 		List<DataObject> doList = attachHibernateTemplate.find( 				"from DataObject where surname = ?", surname); 		if (doList == null || doList.size() <= 0) 			return null; 		return doList.get(0); 	}  	@Override 	public List<DataObject> searchObjectsByName(String query) { 		if (query == null || query.trim().equals("")) 			return attachHibernateTemplate 					.find("from DataObject order by id desc limit 20"); 		return attachHibernateTemplate.find( 				"from DataObject where name like ?", "%" + query + "%"); 	}  } 

В сервисе делегируем все методы из дао

AttachmentService.java

package ru.cpro.uchteno.service.attach;  import java.util.List;  import ru.cpro.uchteno.domain.attach.DataObject;  public interface AttachmentService {      public DataObject getObjectByID(Integer id);          public void saveOrUpdate(DataObject dao);          public void deleteDataObject(DataObject dao);      public List<DataObject> listObjects();      public DataObject getObjectBySurname(String surname);          public List<DataObject> searchObjectsByName(String query);      } 

AttachmentServiceImpl.java

package ru.cpro.uchteno.service.attach;  import java.util.List;  import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service;  import ru.cpro.uchteno.dao.attach.AttachmentDAO; import ru.cpro.uchteno.domain.attach.DataObject;  @Service public class AttachmentServiceImpl implements AttachmentService {          @Autowired     private AttachmentDAO attachmentDAO;          @Override     public List<DataObject> listObjects() { 	return attachmentDAO.listObjects();     }      @Override     public DataObject getObjectByID(Integer id) { 	return attachmentDAO.getObjectByID(id);     }      @Override     public void saveOrUpdate(DataObject dao) { 	attachmentDAO.saveOrUpdate(dao);     }      @Override     public void deleteDataObject(DataObject dao) { 	attachmentDAO.deleteDataObject(dao);     }          @Override     public DataObject getObjectBySurname(String surname) { 	return attachmentDAO.getObjectBySurname(surname);     }      @Override     public List<DataObject> searchObjectsByName(String query) { 	return attachmentDAO.searchObjectsByName(query);     }  } 

Всё, что нужно у нас есть. Теперь осталось только сделать контроллер:

AttachmentController.java

package ru.cpro.uchteno.web.attachment;  import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.sql.Blob; import java.sql.SQLException; import java.util.HashMap; import java.util.List;  import javax.servlet.http.HttpServletResponse;  import org.hibernate.Hibernate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.multipart.MultipartFile;  import ru.cpro.uchteno.domain.attach.DataObject; import ru.cpro.uchteno.service.attach.AttachmentService;  @Controller() @RequestMapping("/") public class AttachmentController { 	//Подключаем наш сервис 	@Autowired 	private AttachmentService attachmentService; 	//Вывод на страницу всех файлов в базе 	@RequestMapping("/admin/dataObjects/private") 	public String index(HashMap<String, Object> map) { 		List<DataObject> doList = attachmentService.listObjects(); //список файлов 		map.put("dlist", doList);// заталкать его в кодель фтраницы 		return "admin/attach/attach";// вернуть путь к вьюхе 	}  	//Добавление файла 	@RequestMapping("/admin/dataObjects/private/add") 	public String add( 			HashMap<String, Object> map, 			@RequestParam(value = "addName", required = true) String name, 			@RequestParam(value = "addFile", required = true) MultipartFile file, 			@RequestParam(value = "addSurname", required = true) String surname) { 		try { 			DataObject dob = new DataObject();//Создать новый файл 			if (name == null || name.trim().equals("")) //Если имя файла пустое 				dob.setName(file.getOriginalFilename()); // Взять имя из  загружаемого файла 			else 				dob.setName(name); // иначе дать файлу новое имя 			Blob blob = Hibernate.createBlob(file.getInputStream()); //Создаем блоб по входному потоку 			dob.setData(blob); 			dob.setContentType(file.getContentType()); // Контент тайп 			dob.setSurname(surname); // задаем файлу “фамилию” 			attachmentService.saveOrUpdate(dob); // сохраняем файл 		} catch (IOException e) { 			System.out.println("Не удалось записать объект в базу"); 			return "redirect:/admin/attach/dataObjects/private/"; 		} 		return "redirect:/admin/dataObjects/private/"; 	}         //Глушим файлы по айдишнику 	@RequestMapping("/admin/dataObjects/private/del") 	public @ResponseBody 	String del(@RequestParam(value = "id", required = true) Integer id) { 		try { 			DataObject dao = attachmentService.getObjectByID(id); 			attachmentService.deleteDataObject(dao); 		} catch (Exception ex) { 			return "FAIL"; 		} 		return "SUCCESS"; 	}  //Скачивание файлов по фамилии. Публичная часть. 	@RequestMapping("/public/dataObjects/getObject") 	public void getObject( 			@RequestParam(value = "s", required = false) String surname, 			HttpServletResponse resp) { 		if (surname == null || surname.trim().equals("")) { 			try { 				resp.getOutputStream().close(); 				return; 			} catch (IOException e) { 				e.printStackTrace(); 			} 		} 		DataObject dao = attachmentService.getObjectBySurname(surname); // Ищем файл по фамилии 		if (dao == null) { 			try { 				resp.getOutputStream().close(); 			} catch (Exception ex) { 			} 			return; 		} 		// наращиваем счетчик скачиваний 		if (dao.getAccessCount() == null) 			dao.setAccessCount(1);  		else 			dao.setAccessCount(dao.getAccessCount() + 1); 		attachmentService.saveOrUpdate(dao);  		//Отдаем юзеру файл 		Blob blob = dao.getData(); 		try { 			InputStream is = blob.getBinaryStream(); 			OutputStream os = resp.getOutputStream(); 			resp.setContentType(dao.getContentType()); 			int n = 0; 			byte buff[] = new byte[1024]; 			while (n >= 0) { 				n = is.read(buff); 				if (n > 0) 					os.write(buff, 0, n); 			} 			is.close(); 			os.close(); 		} catch (Exception e) { 			e.printStackTrace(); 		} 	}  } 

Вариант 2 — Файловое хранилище с кластеризацией

Я решил использовать grails. Но когда пишем на groovy, Blob не работает адекватно. Поэтому разобьем наш файл на кластеры и сохраним по-отдельности.

В принципе тут всё делается практически так же, как и в предыдущем примере. Приложение доступно здесь.

Спасибо всем за внимание! Надеюсь, что мои примеры будут вам полезны.

ссылка на оригинал статьи http://habrahabr.ru/post/225085/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *