Редко какой ADF проект обходится без работы с файлами.
В этой статье мы создадим проект, в котором будет реализована возможность добавления в базу изображений(фотографию сотрудника), их просмотр и скачивание на компьютер.
Приступим.
Будем использовать тестовую схему Oracle (Scott\tiger). В ней есть таблица EMP (сотрудники)
Для работы с файлами существующих таблиц недостаточно. Нужно добавить поле IMAGE_ID в таблицу EMP, создать таблицу PHOTO (с структурой указанной ниже) и связать эти таблицы связью 1 к 1 по полям IMAGE_ID.
Скрипт для создания таблицы PHOTO:
Скрипт для добавления поля ID
В этой статье мы создадим проект, в котором будет реализована возможность добавления в базу изображений(фотографию сотрудника), их просмотр и скачивание на компьютер.
Приступим.
Будем использовать тестовую схему Oracle (Scott\tiger). В ней есть таблица EMP (сотрудники)
Для работы с файлами существующих таблиц недостаточно. Нужно добавить поле IMAGE_ID в таблицу EMP, создать таблицу PHOTO (с структурой указанной ниже) и связать эти таблицы связью 1 к 1 по полям IMAGE_ID.
Скрипт для создания таблицы PHOTO:
CREATE TABLE PHOTO
(
IMAGE_ID NUMBER(4, 0) NOT NULL
,
IMAGE_NAME VARCHAR2(250 BYTE)
,
TYPE VARCHAR2(50 BYTE)
,
DATA BLOB
,
CONSTRAINT PHOTO_PK PRIMARY KEY
(
IMAGE_ID
)
ENABLE
) ;
Скрипт для добавления поля ID
ALTER TABLE EMP ADD (IMAGE_ID NUMBER(4) );
ALTER TABLE EMP
ADD CONSTRAINT EMP_PHOTO_FK1 FOREIGN KEY
(
IMAGE_ID
)
REFERENCES PHOTO
(
IMAGE_ID
)
ENABLE;
Таблица PHOTO имеет 4 поля:
- IMAGE_ID- ID фото
- IMAGE_NAME-название файла
- TYPE-тип файла в MIME формате.
- DATA -поле с типом BLOB, в котором хранится сам файл.
База подготовлена. Теперь приступим к созданию самой формы.
Создайте Fusion Web Application, connection к базе и на основе таблиц EMP и Photo создайте Entitiy Object-ы(EO) , View Object-ы(VO) и View Link(VL) для связи созданных VO.
Для вывода сотрудников создайте jspx страницу (например empPhoto.jspx) и добавьте таблицу сотрудников c VO связанной с фотографиями( в нашем случае EmpView2)
Добавьте следующие Binding's :
Из итератора фотографий связанной с сотрудниками (в нашем случае PhotoView2Iterator)
-ImageName;
-Type;
-Data;
-CreateInsert (Добавление записи в таблицу с фото).
Из итератора сотрудников связанной с фотографиями(в нашем случае EmpView2Iterator)
-ImageId;
-Empno.
Так же добавьте операцию Commit для сохранения изменений в БД.
Пример добавления Bindigs-ов показан ниже:
В созданную таблицу добавьте колонку и вставьте туда кнопку, по нажатии которой будет выводиться popup с фотографией,кнопкой для скачивания и добавления(обновления фото)
В коде страницы это будет выглядеть так:
<af:commandButton text="..." id="cb1" clientComponent="true">
<af:showPopupBehavior triggerType="click" popupId=":p1" align="afterEnd"/>
<af:setPropertyListener type="action" from="#{row.ImageId}"
to="#{pageFlowScope.ifbean.ids}"/>
</af:commandButton>
где af:commandButton - кнопка
af:showPopupBehavior - компонент для связывания кнопки и popup
af:setPropertyListener - сохранение id фото в bean-е.
Код popup будет выглядеть так:
<af:popup childCreation="deferred" autoCancel="disabled" id="p1"
contentDelivery="immediate">
<af:dialog id="d2" type="ok" dialogListener="#{pageFlowScope.ifbean.okClick}">
<f:facet name="buttonBar"/>
<af:image id="i1" source="/imageservlet?id=#{pageFlowScope.ifbean.ids}"
inlineStyle="width:150px; height:150px;"/>
<af:inputFile id="if1" valueChangeListener="#{pageFlowScope.ifbean.UploadListener}"/>
<af:outputText value="#{bindings.ImageName.inputValue}" id="ot9"/>
<af:commandButton text="Скачать" id="cb2">
<af:fileDownloadActionListener contentType="#{bindings.Type.inputValue}"
filename="#{bindings.ImageName.inputValue}"
method="#{pageFlowScope.ifbean.downloadImage}"/>
</af:commandButton>
</af:dialog>
</af:popup>
Рассмотрим каждый компонент подробней:
1. af:image - вывод изображений по свойству source
<af:image id="i1" source="/imageservlet?id=#{pageFlowScope.ifbean.ids}"
inlineStyle="width:150px; height:150px;"/>
В Oracle ADF нет стандартных средств для вывода изображение из базы данных. И что бы изображение отображалась, нам нужно создать сервлет c url pattern /imageservlet и в которых входящими данными будет id изображения. Код сервлета должен быть следующим :package view;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.SQLException;
import javax.servlet.*;
import javax.servlet.http.*;
import oracle.jbo.ApplicationModule;
import oracle.jbo.Row;
import oracle.jbo.client.Configuration;
import oracle.jbo.domain.BlobDomain;
import oracle.jbo.domain.Number;
import oracle.jbo.server.ViewObjectImpl;
public class ImageServlet
extends HttpServlet
{
private static final String CONTENT_TYPE =
"image/jpg; charset=windows-1252";
@SuppressWarnings("compatibility:-2491699079303340020")
private static final long serialVersionUID = 1L;
public void init(ServletConfig config)
throws ServletException
{
super.init(config);
}
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
try {
response.setContentType(CONTENT_TYPE);
response.setContentType(CONTENT_TYPE);
Number idPhoto=null;
//условие по которому при пустом значении id, ему присваивается -1. Для вывода картинки "No photo" (Необходимо, что бы в таблице PHOTO было поле с id=-1 и изображением(например NO PHOTO)) if (request.getParameter("id").equals("")) { idPhoto= new Number(-1); }else idPhoto= new Number(request.getParameter("id")); OutputStream os = response.getOutputStream(); String amDef = "model.AppModule"; // Адрес Application Module String config = "AppModuleLocal";// Выбор необходимой конфигурации в Application Module ApplicationModule am = Configuration.createRootApplicationModule(amDef, config); ViewObjectImpl vo = (ViewObjectImpl) am.findViewObject("PhotoView1"); // Поиск VO в AppModule c фотографиями(VO который не связан с EmpView vo.defineNamedWhereClauseParam("paramIdPhoto", null, null); vo.setWhereClause("IMAGE_ID = :paramIdPhoto"); // создание условия по которому выбирается поле с необходимым нам фото vo.setNamedWhereClauseParam("paramIdPhoto", idPhoto); vo.executeQuery(); Row product = vo.first(); if (product!=null) { BlobDomain image = (BlobDomain) product.getAttribute("Data"); InputStream is = image.getInputStream(); byte[] buffer = new byte[10 * 1024]; int nread; while ((nread = is.read(buffer)) != -1) os.write(buffer, 0, nread); } os.close(); vo.setWhereClause(null); vo.removeNamedWhereClauseParam("paramIdPhoto"); Configuration.releaseRootApplicationModule(am, false); } catch (SQLException e) { System.out.println("SQLException"+e.getMessage());
}
}
}
2. af:inputFile - этот компонент используется для работы с файлами.Необходимо valueChangeListener и добавить в туда следующий код:
private BlobDomain data;
private String type;
private String name;
public void UploadListener(ValueChangeEvent valueChangeEvent) {
// при добавлении файла срабатывает valueChangeListener , в который передается файл. Мы сохраняем файл, имя и тип в созданные нами переменные, что бы потом добавить или обновить фотографию
UploadedFile file = (UploadedFile) valueChangeEvent.getNewValue();
name = file.getFilename();
type = ContentTypes.get(name); // Создайте класс ContentTypes с кодом написанным ниже
data=createBlobDomain(file);
}
private BlobDomain createBlobDomain(UploadedFile file)
{
InputStream in = null;
BlobDomain blobDomain = null;
OutputStream out = null;
try
{
in = file.getInputStream();
blobDomain = new BlobDomain();
out = blobDomain.getBinaryOutputStream();
IOUtils.copy(in, out); // Эту библиотеку необходима скачать отсюда: http://commons.apache.org/proper/commons-io/download_io.cgi и добавить в Libraries and Classpath проекта
}
catch (SQLException e)
{
e.fillInStackTrace();
} catch (Exception e) {
System.out.println(e.getMessage());
}
return blobDomain;
}
public class ContentTypes {
public static String get(String fileName)
{
String mime = null;
String ext = fileName.toLowerCase();
if (ext.endsWith(".jpg"))
{
mime = "image/jpeg";
}
else if (ext.endsWith(".jpeg"))
{
mime = "image/jpeg";
}
else if (ext.endsWith(".png"))
{
mime = "image/png";
}
return mime;
}
}
4. af:fileDownloadActionListener - компонент добавляется для сохранения файла на ПК. Он имеет три свойства:contentType - тип содержимого в Mime формате (пр. image/jpeg);
filename - имя файла для сохранения;
method - метод для сохранения данных. Он имеет два аргумента: FacesContext и OutputStream. Все данные записанные в OutputStream сохраняются в файл с именем filename и типом contentType.
Ниже приведен код метода для сохранения фото при помощи af:fileDownloadActionListener
public void downloadImage(FacesContext facesContext, OutputStream outputStream)
{
BindingContainer bindings = BindingContext.getCurrent().getCurrentBindingsEntry();
AttributeBinding attr = (AttributeBinding) bindings.getControlBinding("Data");
if (attr == null)
{
return;
}
BlobDomain blob = (BlobDomain) attr.getInputValue();
try
{
IOUtils.copy(blob.getInputStream(), outputStream);
blob.closeInputStream();
outputStream.flush();
}
catch (IOException e)
{
e.printStackTrace();
FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, e.getMessage(), "");
FacesContext.getCurrentInstance().addMessage(null, msg);
}
}
Так же наш popup имеет компонент dialog с листенером на нажатие диалоговых кнопок
<af:dialog id="d2" type="ok" dialogListener="#{pageFlowScope.ifbean.okClick}">
Код ниже добавляет новое фото, если до этого изображение отсутствовало у сотрудника, и обновляет если изображение уже было.
Готово.
public void okClick(DialogEvent dialogEvent) {
DCBindingContainer lBindingContainer =
(DCBindingContainer) BindingContext.getCurrent().getCurrentBindingsEntry();
DCIteratorBinding lBinding = lBindingContainer.findIteratorBinding("PhotoView2Iterator");
AttributeBinding id = (AttributeBinding)lBindingContainer.getControlBinding("Empno");
AttributeBinding attribute = (AttributeBinding) lBindingContainer.getControlBinding("ImageId");
OperationBinding createPhoto=lBindingContainer.getOperationBinding("CreateInsert");
OperationBinding commit=lBindingContainer.getOperationBinding("Commit");
Row newRow = lBinding.getCurrentRow();
if (newRow==null) {
createPhoto.execute();
newRow = lBinding.getCurrentRow();
newRow.setAttribute("ImageId", id.getInputValue());
}
newRow.setAttribute("ImageName", name);
newRow.setAttribute("Data", data);
newRow.setAttribute("Type", type);
commit.execute();
attribute.setInputValue(id);
commit.execute();
}
Компонент af:form у jspx страницы обязательно должен иметь свойство usesUpload="true", иначе af:inputFile не будет работать:
<af:form id=
"f1"
usesUpload=
"true"
>
Комментариев нет:
Отправить комментарий