Cfile savefile не сохраняет файл

Обновлено: 04.07.2024

В данной статье рассказывается как открывать и закрывать файлы, а так же как считывать и записывать в них данные. Класс CFile обеспечивает основные бинарные оперции с файлами. Классы CStdioFile , CMemFile наследованы от CFile и используются для более специфической работы с файлами.

В MFC процесс открытия файла состоит из двух этапов.

    Сперва необходимо создать объект файла без указания пути или флагов доступа (чтение, запись и т.д.).

Обычно объект файла создаётся путём объявления переменной типа CFile .

Если файл был открыт успешно, то функция Open вернёт ненулевое значение, иначе, в случае ошибки будет возвращен 0 (ноль). Прототип функции Open выглядит следующим образом:

В параметре nOpenFlags передаются флаги доступа для файла, такие как "только чтение" (read-only) и т.д. Возможные значения флагов определены в виде констант в классе CFile , и использовать их можно с приставкой " CFile:: " - например CFile::modeRead . Для создания файла используется флаг CFile::modeCreate .

Следующий пример показывает, как создать новый файл с правами на чтение/запись (существующий файл с таким же путём будет заменён):

Заметка: Данный пример создаёт и открывает файл. Если возникает какая-либо ошибка, то вызов Open может вернуть объект CFileException в своём последнем параметре. Макрос TRACE распечатывает как имя файла, так и код, указывающий на причину ошибки. Если необходимо узнать более подробно о возникшей ошибке, то можно воспользоваться функцией AfxThrowFileException .

Чтение и запись файлов

Теперь давайте посмотрим, как напрямую считывать и записывать в объект CFile (для буфферизованного ввода/вывода используется класс CArchive ).

  • Используются методы Read и Write соответственно. А так же, для перемещения указателя внутри файла используется метод Seek .

Read имеет два параметра (первый, это указатель на буфер, а второй, это количество байт, которые необходимо считать) и возвращает количество байт, которые были реально считаны. Например, если был достигнут конец файла (end-of-file (EOF)), то функция вернёт количество байт, оставшихся до конца файла. Если при чтении из файла произойдёт ошибка, то возникнет исключительная ситуация. Write очень похожа на Read , за исключением того, что не возвращает количество записанных байт. Так же при возникновении ошибки произойдёт исключение. Следующий пример показывает, как читать и записывать в объект CFile :

Заметка: Исключительные ситуации при операциях чтения/записи обрабатываются при помощи блока try / catch . Более подробно см. Обработка исключений (MFC).

Когда работа с файлом завершена, необходимо закрыть его.

  • Используется метод Close . Эта функция закрывает файл и, в случае необходимости, сбрасывает в него буфер.

Если Вы создали объект CFile в классе или функции, то объект будет автоматически закрыт и уничтожен как только выполнение кода выйдет за пределы видимости объекта. Обратите внимание на то, что удаление объекта CFile не приведёт к физическому удалению файла с диска.

Получение информации о файле

CFile так же позволяет получить довольно обширную информацию о файлах, включая существование файла, дату и время создания и изменения, размер и путь.

  • Воспользуйтесь статическим методом GetStatus . GetStatus вернёт 0 если файл не существует.

Таким образом, можно использовать результат функции GetStatus , чтобы определить, нужно ли использовать флаг CFile::modeCreate при открытии файла. Следующий пример демонстрирует это:

По умолчанию для всех файлов инфоблоков используется общая папка /upload/iblock/. Файлы сохраняются в подпапки, сгенерированные случайным образом, например, /upload/iblock/ff7/.
Как следствие, все файлы доступны по прямой ссылке. В целом, в этом нет ничего страшного, так как итоговый путь (при условии, что файл отдается через php) сложно подобрать. Но в некоторых случаях требуются дополнительные меры безопасности.

Основная идея – как-то вынести файлы инфоблока в отдельную подпапку и закрыть её с помощью стандартных механизмов nginx / apache.
Небольшим дополнительным плюсом будет то, что можно достаточно просто посмотреть, сколько места занимают файлы отдельного инфоблока.

Суть в следующем: в методе CFile::SaveFile второй параметр ($strSavePath) отвечает за директорию внутри папки upload. Инфоблоки его передают как «iblock», нам надо его заменить.

  1. Код, приведенный ниже не рекомендуется к использованию, но в целом он позволяет решить задачу (и, в том числе, работает, как в публичной, так и в административной части).
  2. Случай, когда файлы хранятся в облаке, не рассматривается. . В случае включения в код "как-есть" необходимо прописать пути в методе loadPaths

Общая схема

Регистрируем обработчик на событие OnFileSave. Обработчик будет вызван при сохранении файла. В обработчике каким-то способом получаем ID инфоблока, файл которого сохраняется. Сохраняем файл физически и возвращаем true.

    Регистрируем обработчик на событие OnFileSave модуля main.

  • Во-первых, как в методе CFile::SaveFile, так и в обработчике у нас нет идентификатора инфоблока.
  • Во-вторых, $strSavePath в обработчик передается по значению, а не по ссылке, поэтому изменить его просто так не получится.
  • Во-третьих, обработчик позволяет только определить собственный способ физического сохранения файла, в таблицу b_file пишет сам метод CFile::SaveFile.
  • Путь 1: Значение из $_REQUEST.
    При редактировании элемента в административной части url имеет вид /bitrix/admin/iblock_element_edit.php?IBLOCK_ID=170&type=catalog&ID=388118&lang=ru&find_section_section=-1&WF=Y
    и, соответственно, в $_REQUEST у нас есть IBLOCK_ID
    Этот метод не универсален, т.к. не поддерживает добавление с помощью API (например, из публичной части).
    Кроме того, при сохранении элемента может возникнуть ситуация, когда надо добавить элемент с файлом в другой инфоблок.
  • Путь 2: Сохранение инфоблока во время вызова события в статический член класса
    На событиях обновления OnBeforeIBlockElementUpdate, OnAfterIBlockElementUpdate и OnIBlockElementSetPropertyValues сохранять в статический член класса ID инфоблока.
    В данном случае нет нужного события (onBefore) для метода CIBlockElement::SetPropertyValuesEx
    Кроме того, проблема с добавлением элемента с файлом в другой инфоблок также остается
  • Путь 3: Через функцию debug_backtrace().
    Это ресурсозатратно, но узнать ID инфоблока можно более точно, чем в предыдущих методах.

Все новые файлы нужных нам инфоблоков с этого момента будут в отдельной папке.

2013_03_31_14_55_33.jpg

Не так давно в свет вышел довольно интересный и красивый загрузчик файлов, который сейчас во всю используется в блогах и живой ленте.

А вот описания к нему я не нашел. Восполним пробел.

Компонент называется main.file.input и лежит в системных компонентах. Все что он умеет - это загружать и сохранять файлы через CFile::SaveFile(), удалять через CFile::Delete() и все. Файлы по дефолту привязываются к главному модулю main и сохраняются в одноименной папке относительно /upload. Сам компонент возвращает некий универсальный идентификатор. Без него невозможна работа callback-ов при загрузки файла и после.

Сценарий работы компонента
Все основные js-функции лежат в script.js. При загрузке страницы формируется js-объект с заданными параметрами (это происходит в шаблоне). Далее, либо при загрузке страницы, либо по событию (клик, аякс-загрузка контента, ховер и пр.) срабатывают кастомные js-события, они вызывают функции, которые открывают нужные дивы и таблицы, ибо, по дефолту они скрыты. При этом срабатывает функция window.BlogBFileDialogUploader.prototype.GetUploadFileName, которая формирует форму для отправки файла и фрейм.

При отправке формы срабатывает window.BlogBFileDialogUploader.prototype.CallSubmit, которая отправляет файл. По дефолту файл отправляется на ту страницу, на которой находится сам компонент. Путь берется из параметра this.uploadFileUrl, а он из параметров при создании объекта. При случае можно менять на какой угодно. В общем и целом, лучше чтоб файл загружался на страницу, где находится компонент, потому как в самом компоненте идет проверка основных параметров и там формируется объект для работы с callback-ами. Без него у вас файл может загрузиться и сохраниться нормально, но индикатор загрузки зависнет, как будто файл не загрузился и процесс завис. Но обо всем по порядку.

Пользование компонента.
Вызываем компонент:

Параметры:
INPUT_NAME // уникальный name инпута, без него выдает ошибку
MULTIPLE => Y || N - позволяет или не позволяет множественную загрузку
MODULE_ID => main || iblock || blog || forum и пр. имя модуля, к которому файл привязан будет и в какую папку попадет относительно upload.
MAX_FILE_SIZE // максимальный размер файла (вроде в байтах)
ALLOW_UPLOAD A || F || I - какой тип файлов будем грузить: F - файлы, I - картинки, A - все подряд.
ALLOW_UPLOAD_EXT => «*.zip,*.rar,*.doc и пр.» // какие расширения файлов можно грузить. Работает если ALLOW_UPLOAD => F

:)

После этого на странице вы не увидите ничего Для этого надо в шаблоне избавиться от некоторых мало понятных проверок и функции.

Убираем php-вставки в скрипте, в шаблоне, примерно строки 117, 154 и 171. Тут идет проверка php - callback - функции, в момент загрузки страницы, она пустая и компонент скрыт. Активируется после перетаскивания файла в дропзону. После этого компонент нормально отображается на странице при ее загрузке.

Сам загрузчик формируется объектом:

Если посмотреть функцию window.BlogBFileDialogUploader.prototype.CreateElements - там формируется фрейм и форма для отправки файла. Если нужно вместе с файлом передать еще какие-либо параметры, то добавляем свои инпуты.

window.BlogBFileDialogUploader.prototype.Callback - срабатывает после загрузки и содержит массив с инфой о файле. Сюда можно впихнуть все что получится в $arResult. С этим массивом можно работать и использовать дальше после загрузки.

При использовании компонента в режиме ajax - т.е. при загрузке компонента на страницу или в popup-окно с помощью аякса, не забудьте поменять параметр "upload_path" в объекте формирования загрузчика в шаблоне компонента. И если не хочется заморачиваться файлом-приемником, то поставьте туда этот же компонент.

Некоторые сведения относятся к предварительной версии продукта, в которую до выпуска могут быть внесены существенные изменения. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.

Сохраняет содержимое элемента управления RichTextBox в файл.

Перегрузки

Сохраняет содержимое элемента управления RichTextBox в открытый поток данных.

Сохраняет содержимое элемента управления RichTextBox в файл определенного типа.

Сохраняет содержимое элемента управления RichTextBox в RTF-файл.

SaveFile(Stream, RichTextBoxStreamType)

Сохраняет содержимое элемента управления RichTextBox в открытый поток данных.

Параметры

Поток данных, который содержит файл, в который будут сохранены данные.

Одно из значений перечисления RichTextBoxStreamType.

Исключения

В качестве параметра fileType указан недопустимый тип файла.

Во время сохранения содержимого элемента управления в файл произошла ошибка.

Примеры

В следующем примере кода показано использование SaveFile методов и LoadFile с потоками. Он также демонстрирует использование FileDialog.FileName элементов, FileDialog.DefaultExt , SaveFileDialog.CreatePrompt и SaveFileDialog.OverwritePrompt .

Это полный пример, готовый к запуску при копировании в проект.

Комментарии

Эта версия SaveFile метода позволяет сохранить все содержимое элемента управления в уже открытом потоке данных. Затем поток данных может сохранить данные в файл. Можно использовать LoadFile метод для загрузки содержимого файла в RichTextBox .

Эта версия SaveFile метода также позволяет указать формат данных информации, которая будет отправлена в Stream объект.

См. также раздел

Применяется к

SaveFile(String, RichTextBoxStreamType)

Сохраняет содержимое элемента управления RichTextBox в файл определенного типа.

Параметры

Имя и расположение сохраняемого файла.

Одно из значений перечисления RichTextBoxStreamType.

Исключения

В качестве параметра fileType указан недопустимый тип файла.

Во время сохранения содержимого элемента управления в файл произошла ошибка.

Примеры

В следующем примере кода содержимое в RichTextBox текстовом файле ASCII сохраняется. В примере класс используется SaveFileDialog для вывода диалогового окна для запроса пути и имени файла от пользователя. Затем код сохраняет содержимое элемента управления в этот файл. В примере используется эта версия метода, SaveFile чтобы указать, что файл должен быть сохранен как текстовый файл в кодировке ASCII, а не в стандартном формате форматированного текста. В этом примере код должен размещаться в Form классе, который имеет RichTextBox элемент управления с именем richTextBox1 .

Комментарии

SaveFileметод позволяет сохранить все содержимое элемента управления в RTF файле, который может использоваться другими программами, такими как Microsoft Word и Windows WordPad. Если имя файла, передаваемое в параметр, path уже существует в указанном каталоге, файл будет перезаписан без уведомления. Можно использовать LoadFile метод для загрузки содержимого файла в RichTextBox .

Читайте также: