I've found various answers on how to upload assets via PHP for Craft 2, however nothing for Craft 3.

All answers use the method:


E.g: How to upload an asset from a front-end form

I was wondering what the equivalent is for Craft 3.


2 Answers 2


I finally figured out how to upload an asset, however I still don't understand why the insertFileByLocalPath has been removed.

 * @param UploadedFile $uploadedFile
 * @param int $folderId
 * @return Asset
 * @throws BadRequestHttpException
 * @throws UploadFailedException
protected static function uploadNewAsset(UploadedFile $uploadedFile, $folderId) {

    if (empty($folderId)) {
        throw new BadRequestHttpException('No target destination provided for uploading');

    if ($uploadedFile === null) {
        throw new BadRequestHttpException('No file was uploaded');

    $assets = Craft::$app->getAssets();

    if ($uploadedFile->getHasError()) {
        throw new UploadFailedException($uploadedFile->error);

    // Move the uploaded file to the temp folder
    if (($tempPath = $uploadedFile->saveAsTempFile()) === false) {
        throw new UploadFailedException(UPLOAD_ERR_CANT_WRITE);

    if (empty($folderId)) {
        throw new BadRequestHttpException('The target destination provided for uploading is not valid');

    $folder = $assets->findFolder(['id' => $folderId]);

    if (!$folder) {
        throw new BadRequestHttpException('The target folder provided for uploading is not valid');

    // Check the permissions to upload in the resolved folder.
    $filename = Assets::prepareAssetName($uploadedFile->name);

    $asset = new Asset();
    $asset->tempFilePath = $tempPath;
    $asset->filename = $filename;
    $asset->newFolderId = $folder->id;
    $asset->volumeId = $folder->volumeId;
    $asset->avoidFilenameConflicts = true;

    $result = Craft::$app->getElements()->saveElement($asset);

    return $asset;

and then in my controller I can simply use

$service->uploadImage(UploadedFile::getInstanceByName('photo'), 1);
  • insertFileByLocalPath() was a casualty of a lot of element save refactoring. Basically in Craft 3, we want all element saves to go through craft\services\Elements::saveElement(). Jul 7, 2017 at 12:46
  • Fair enough, any plans to add helper methods like above to make it easier for people to use? The code above isn't the most intuitive.
    – SteveEdson
    Jul 7, 2017 at 12:50
  • I don't think so, as it only helps a pretty narrow use case. What if someone wants to set content on the asset? etc. Jul 7, 2017 at 12:52

Not an actual answer to your question, but more of a pointer in the general direction.

Docs for Craft 3 can be found on Github, they are a work in progress by the looks of things.

In particular you'll probably want to look at the Plugin Development - Services section.

Now together with the above docs, and a fresh install of Craft 3 itself, you should be able to find what you are looking for in the Assets Service. Which you'll find in the following location of you install:


The saveAsset method looks like it might be a good candidate for what you are doing.

Hopefully that should help a little bit.

Just so you know, I'm not being deliberately vague. I'm only just starting to look into Craft 3 and was having a browse around the source code myself and it looked like it might help :)

  • 2
    Thanks, I appreciate the help. I'll update my question when I get home, I've since figured out how to save an uploaded asset, and I've written a helper method for it. It seems very complicated though, so I'm still wondering where the previous helper method from Craft 2 disappeared to.
    – SteveEdson
    Jun 27, 2017 at 15:10
  • I've added an answer with my code for uploading a new asset. (Did it as a new answer since code in comments doesn't work)
    – SteveEdson
    Jun 28, 2017 at 8:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.